JavaRush /Java Blog /Random-KO /Java의 멀티스레딩: 본질, 장점 및 일반적인 함정

Java의 멀티스레딩: 본질, 장점 및 일반적인 함정

Random-KO 그룹에 게시되었습니다
안녕하세요! 우선, 축하합니다. Java의 멀티스레딩 주제에 도달하셨습니다! 이것은 심각한 성과이며, 갈 길이 멀다. 하지만 준비하세요. 이것은 이 과정에서 가장 어려운 주제 중 하나입니다. 그리고 여기서는 복잡한 클래스나 많은 메서드가 사용된다는 것이 요점이 아닙니다. 반대로 24개도 없습니다. 무엇보다 생각을 조금 바꿔야 합니다. 이전에는 프로그램이 순차적으로 실행되었습니다. 일부 코드 줄은 다른 코드를 따르고 일부 메서드는 다른 코드를 따랐으며 전반적으로 모든 것이 명확했습니다. 먼저 무언가를 계산한 다음 콘솔에 결과를 표시하고 프로그램을 종료합니다. 멀티스레딩을 이해하려면 동시성 측면에서 생각하는 것이 가장 좋습니다. 아주 간단한 것부터 시작해 보겠습니다 :) Java의 멀티스레딩: 본질, 장점 및 일반적인 함정 - 1가족이 한 집에서 다른 집으로 이사한다고 상상해 보세요. 이사에서 중요한 부분은 책을 싸는 것입니다. 당신은 책을 많이 모아서 상자에 넣어야 합니다. 이제 당신만이 자유롭습니다. 엄마는 음식을 준비하고 있고, 오빠는 옷을 모으고 있고, 언니는 가게에 갔어요. 적어도 혼자 관리할 수 있으며 조만간 작업을 직접 완료할 수도 있지만 시간이 많이 걸립니다. 그러나 20분 후에 당신의 여동생이 가게에서 돌아올 것이고 그녀는 더 이상 할 일이 없습니다. 그래서 그녀는 당신과 함께 할 수 있습니다. 작업은 동일하게 유지되었습니다. 책을 상자에 넣는 것입니다. 단지 두 배 빠른 속도로 실행됩니다. 왜? 작업이 병렬로 수행되기 때문입니다. 두 개의 서로 다른 "스레드"(당신과 당신의 여동생)가 동일한 작업을 동시에 수행하고 있으며 아무것도 변경되지 않으면 모든 것을 혼자 수행하는 상황에 비해 시간 차이가 매우 커집니다. 당신의 형제가 곧 임무를 완수한다면, 그는 당신을 도울 수 있고 일이 훨씬 더 빨리 진행될 것입니다.

멀티스레딩이 Java에서 해결하는 문제

기본적으로 Java 멀티스레딩은 두 가지 주요 문제를 해결하기 위해 개발되었습니다.
  1. 동시에 여러 작업을 수행합니다.

    위의 예에서 서로 다른 스레드(예: 가족 구성원)는 여러 작업을 병렬로 수행했습니다. 즉, 설거지, 상점에 가기, 물건 접기 등이 있습니다.

    좀 더 "프로그래머"적인 예를 들 수 있습니다. 사용자 인터페이스가 있는 프로그램이 있다고 상상해 보십시오. 계속 버튼을 클릭하면 프로그램 내에서 일부 계산이 수행되어야 하며 사용자에게는 다음 인터페이스 화면이 표시됩니다. 이러한 작업을 순차적으로 수행하는 경우 "계속" 버튼을 클릭하면 프로그램이 정지됩니다. 모든 내부 계산이 완료되고 프로그램이 인터페이스가 그려지기 시작하는 부분에 도달할 때까지 사용자는 "계속" 버튼이 있는 동일한 화면을 보게 됩니다.

    자, 몇 분만 기다리자!

    Java의 멀티스레딩: 본질, 장점 및 일반적인 함정 - 3

    우리는 또한 프로그램을 다시 만들 수도 있고, 프로그래머들이 말하는 것처럼 "병렬화"할 수도 있습니다. 필요한 계산을 한 스레드에서 수행하고 인터페이스 렌더링을 다른 스레드에서 수행하도록 합니다. 대부분의 컴퓨터에는 이를 위한 충분한 리소스가 있습니다. 이 경우 프로그램은 "어리석은" 것이 아니며 사용자는 내부에서 무슨 일이 일어나고 있는지 걱정하지 않고 인터페이스 화면 사이를 침착하게 이동할 것입니다. 방해가 되지 않습니다 :)

  2. 계산 속도를 높입니다.

    여기에서는 모든 것이 훨씬 간단합니다. 프로세서에 여러 개의 코어가 있고 대부분의 프로세서가 이제 멀티 코어인 경우 여러 코어를 통해 작업 목록을 병렬로 해결할 수 있습니다. 분명히, 우리가 1000개의 문제를 해결해야 하고 각 문제를 1초 안에 해결한다면, 하나의 코어는 1000초 안에 목록을 처리하고, 두 개의 코어는 500초 안에, 세 개의 코어는 333초 안에 처리할 것입니다.

그러나 강의에서 이미 읽으셨듯이 현대 시스템은 매우 똑똑하며 하나의 컴퓨팅 코어에서도 작업이 교대로 수행될 때 병렬성 또는 유사 병렬성을 구현할 수 있습니다. 일반적인 내용에서 구체적인 내용으로 이동하여 멀티스레딩과 관련된 Java 라이브러리의 기본 클래스인 java.lang.Thread에 대해 알아 보겠습니다. 엄밀히 말하면 Java의 스레드는 클래스의 인스턴스로 표시됩니다 Thread. 즉, 10개의 스레드를 생성하고 실행하려면 이 클래스의 개체 10개가 필요합니다. 가장 간단한 예를 작성해 보겠습니다.
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("I'm Thread! My name is " + getName());
   }
}
스레드를 생성하고 시작하려면 클래스를 생성하고 java.lang. Thread그 안에 있는 메서드를 재정의합니다 run(). 마지막은 매우 중요합니다. run()스레드가 실행해야 하는 논리를 규정하는 것은 메서드에 있습니다 . 이제 인스턴스를 생성 MyFirstThread하고 실행하면 메서드는 run()이름이 포함된 줄을 콘솔에 인쇄합니다. 메서드는 getName()자동으로 할당되는 스레드의 "시스템" 이름을 인쇄합니다. 그런데 실제로 왜 "만약"일까요? 만들고 테스트해보자!
public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
콘솔 출력: 저는 Thread입니다! 내 이름은 Thread-2입니다. 저는 Thread입니다! 내 이름은 Thread-1입니다. 저는 Thread입니다! 내 이름은 Thread-0입니다. 저는 Thread입니다! 내 이름은 Thread-3입니다. 저는 Thread입니다! 내 이름은 Thread-6입니다. 저는 Thread입니다! 내 이름은 Thread-7입니다. 저는 Thread입니다! 내 이름은 Thread-4입니다. 저는 Thread입니다! 내 이름은 Thread-5입니다. 저는 Thread입니다! 내 이름은 Thread-9입니다. 저는 Thread입니다! 내 이름은 Thread-8입니다. 객체의 메서드를 호출하여 이를 MyFirstThread상속 하고 실행하는 10개의 스레드(객체)를 만듭니다 . 메소드를 호출한 후 해당 메소드가 작동하기 시작 하고 메소드에 작성된 로직이 실행됩니다. 참고: 스레드 이름은 순서가 아닙니다. 꽤 이상합니다. 왜 차례로 실행되지 않았습니까? , , 등? 이것은 표준적인 "순차적" 사고가 작동하지 않는 경우의 정확한 예입니다. 사실 이 경우에는 10개의 스레드를 생성하고 실행하는 명령만 실행합니다. 실행해야 하는 순서는 운영 체제 내부의 특수 메커니즘인 스레드 스케줄러에 의해 결정됩니다. 그것이 얼마나 정확하게 구성되어 있고 어떤 원칙에 따라 결정을 내리는지는 매우 복잡한 주제이므로 지금은 자세히 다루지 않겠습니다. 기억해야 할 가장 중요한 점은 프로그래머가 스레드 실행 순서를 제어할 수 없다는 것입니다. 상황의 심각성을 인식하려면 위 예제의 메서드를 몇 번 더 실행해 보세요. 두 번째 콘솔 출력: I'm Thread! 내 이름은 Thread-0입니다. 저는 Thread입니다! 내 이름은 Thread-4입니다. 저는 Thread입니다! 내 이름은 Thread-3입니다. 저는 Thread입니다! 내 이름은 Thread-2입니다. 저는 Thread입니다! 내 이름은 Thread-1입니다. 저는 Thread입니다! 내 이름은 Thread-5입니다. 저는 Thread입니다! 내 이름은 Thread-6입니다. 저는 Thread입니다! 내 이름은 Thread-8입니다. 저는 Thread입니다! 내 이름은 Thread-9입니다. 저는 Thread입니다! 내 이름은 Thread-7입니다. 세 번째 콘솔 출력: I'm Thread! 내 이름은 Thread-0입니다. 저는 Thread입니다! 내 이름은 Thread-3입니다. 저는 Thread입니다! 내 이름은 Thread-1입니다. 저는 Thread입니다! 내 이름은 Thread-2입니다. 저는 Thread입니다! 내 이름은 Thread-6입니다. 저는 Thread입니다! 내 이름은 Thread-4입니다. 저는 Thread입니다! 내 이름은 Thread-9입니다. 저는 Thread입니다! 내 이름은 Thread-5입니다. 저는 Thread입니다! 내 이름은 Thread-7입니다. 저는 Thread입니다! 내 이름은 스레드-8입니다Threadstart()start()run()Thread-0Thread-1Thread-2main()

멀티스레딩으로 인해 발생하는 문제

책의 예에서 멀티스레딩이 매우 중요한 문제를 해결하고 이를 사용하면 프로그램 작업 속도가 빨라진다는 것을 알았습니다. 많은 경우 - 여러 번. 그러나 멀티스레딩이 복잡한 주제로 간주되는 것은 아무것도 아닙니다. 결국, 잘못 사용하면 문제가 해결되기는커녕 오히려 문제가 발생하게 됩니다. 내가 "문제를 만든다"고 말할 때, 추상적인 것을 의미하는 것은 아닙니다. 멀티스레딩으로 인해 발생할 수 있는 두 가지 특정 문제는 교착 상태와 경쟁 조건입니다. 교착 상태는 여러 스레드가 서로가 점유하는 리소스를 기다리고 있으며 어느 스레드도 계속 실행될 수 없는 상황입니다. 이에 대해서는 향후 강의에서 더 자세히 설명하겠지만 지금은 이 예제로 충분합니다. Java의 멀티스레딩: 본질, 장점 및 일반적인 함정 - 4 thread-1이 일부 Object-1과 작동하고 thread-2가 Object-2와 작동한다고 상상해 보십시오. 프로그램은 다음과 같이 작성됩니다.
  1. Thread-2가 Object 2에 대한 작업을 중지하고 Object-1로 전환하는 즉시 Thread-1은 Object-1에 대한 작업을 중지하고 Object-2로 전환합니다.
  2. Thread-2는 Thread-1이 Object 1과의 작업을 중지하고 Object-2로 전환하자마자 Object-2와의 작업을 중지하고 Object-1로 전환합니다.
멀티스레딩에 대한 깊은 지식이 없어도 아무 것도 나오지 않는다는 것을 쉽게 이해할 수 있습니다. 스레드는 결코 장소를 바꾸지 않으며 영원히 서로를 기다릴 것입니다. 오류는 명백해 보이지만 실제로는 그렇지 않습니다. 프로그램에 쉽게 허용할 수 있습니다. 다음 강의에서는 교착상태를 일으키는 코드의 예를 살펴보겠습니다. 그런데 Quora에는 교착 상태가 무엇인지 설명하는 훌륭한 실제 사례가 있습니다 . “인도의 일부 주에서는 농부로 등록하지 않으면 농지를 팔지 않습니다. 다만, 농지를 소유하지 않은 경우에는 농업인으로 등록되지 않습니다.” 좋아, 내가 뭐라고 말할 수 있니? :) 이제 경쟁 조건 - 경주 상태에 대해 알아보겠습니다. 경쟁 조건은 코드의 일부가 실행되는 순서에 따라 시스템이나 응용 프로그램의 작동이 달라지는 다중 스레드 시스템이나 응용 프로그램의 설계 결함입니다. 스레드를 실행하는 예를 기억하세요. Java의 멀티스레딩: 본질, 장점 및 일반적인 함정 - 5
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("Выполнен поток " + getName());
   }
}

public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
이제 프로그램이 음식을 준비하는 로봇의 작동을 담당한다고 상상해 보세요! Thread-0은 냉장고에서 계란을 꺼냅니다. 스트림 1이 스토브를 켭니다. Stream-2는 프라이팬을 꺼내 스토브 위에 올려 놓습니다. 스트림 3은 스토브에 불을 붙입니다. 스트림 4는 팬에 기름을 붓습니다. 스트림 5는 계란을 깨서 프라이팬에 붓습니다. 스트림 6은 껍질을 쓰레기통에 버립니다. Stream-7은 완성된 스크램블 에그를 불에서 꺼냅니다. 포토크-8은 스크램블 에그를 접시에 담는다. 스트림 9은 설거지를 합니다. 프로그램 결과를 살펴보십시오. Thread-0 실행 Thread-2 스레드 실행 Thread-1 스레드 실행 Thread-4 스레드 실행 Thread-9 스레드 실행 Thread-5 스레드 실행 Thread-8 스레드 실행 Thread-7 스레드 실행 Thread-7 스레드 실행됨 -3 Thread-6 스레드 실행됨 스크립트가 재미있나요? :) 그리고 우리 프로그램의 작동은 스레드가 실행되는 순서에 따라 달라지기 때문입니다. 시퀀스를 조금만 위반하면 우리 주방은 지옥으로 변하고 미친 로봇이 주변의 모든 것을 파괴합니다. 이는 다중 스레드 프로그래밍의 일반적인 문제이기도 하며, 한 번 이상 듣게 될 것입니다. 강의를 마치면서 멀티스레딩에 관한 책을 추천하고 싶습니다.
Java의 멀티스레딩: 본질, 장점 및 일반적인 함정 - 6
"Java Concurrency in Practice"는 2006년에 작성되었지만 관련성을 잃지 않았습니다. 기본부터 시작하여 가장 일반적인 오류 및 안티패턴 목록으로 끝나는 Java의 다중 스레드 프로그래밍을 다룹니다. 멀티스레드 프로그래밍 전문가가 되기로 결정했다면 이 책을 꼭 읽어야 합니다. 다음 강의에서 만나요! :)
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION