자바 스레드의 수명주기

1. 소개

이 기사에서는 스레드의 수명주기 인 Java의 핵심 개념에 대해 자세히 설명합니다.

스레드 실행 중에 이러한 상태를 더 잘 이해하기 위해 빠른 그림 다이어그램과 물론 실용적인 코드 스 니펫을 사용합니다.

Java의 스레드 이해를 시작하려면 스레드 작성에 대한이 기사를 참조하십시오.

2. 자바의 멀티 스레딩

Java 언어에서 멀티 스레딩은 Thread의 핵심 개념에 의해 구동됩니다 . 수명주기 동안 스레드는 다양한 상태를 거칩니다.

3. 자바 스레드의 라이프 사이클

java.lang.Thread의의 클래스는 포함 - 정적 상태 열거 가능성 상태를 정의합니다. 특정 시점 동안 스레드는 다음 상태 중 하나에 만있을 수 있습니다.

  1. NEW – 아직 실행을 시작하지 않은 새로 생성 된 스레드
  2. RUNNABLE – 실행 중이거나 실행할 준비가되었지만 리소스 할당을 기다리고 있습니다.
  3. BLOCKED – 동기화 된 블록 / 메소드에 들어가거나 다시 들어가기 위해 모니터 잠금을 획득하기를 기다리고 있습니다.
  4. WAITING – 다른 스레드가 시간 제한없이 특정 작업을 수행 할 때까지 기다 립니다.
  5. TIMED_WAITING – 다른 스레드가 지정된 기간 동안 특정 작업을 수행 할 때까지 대기
  6. TERMINATED – 실행 완료

이러한 모든 상태는 위의 다이어그램에서 다룹니다. 이제 각각에 대해 자세히 설명하겠습니다.

3.1. 새로운

NEW 스레드 (또는 출생의 스레드 )가 생성되지만 아직 시작되지 않은 것 스레드입니다. start () 메서드를 사용하여 시작할 때까지이 상태로 유지됩니다 .

다음 코드 스 니펫은 NEW 상태 에있는 새로 생성 된 스레드를 보여줍니다 .

Runnable runnable = new NewState(); Thread t = new Thread(runnable); Log.info(t.getState());

언급 된 스레드를 시작하지 않았 으므로 t.getState () 메서드 는 다음을 인쇄합니다.

NEW

3.2. 실행 가능

새 스레드를 생성하고 이에 대해 start () 메서드를 호출하면 NEW 에서 RUNNABLE 상태 로 이동 합니다. 이 상태의 스레드는 실행 중이거나 실행할 준비가되어 있지만 시스템에서 리소스 할당을 기다리고 있습니다.

멀티 스레드 환경에서 Thread-Scheduler (JVM의 일부)는 각 스레드에 고정 된 시간을 할당합니다. 따라서 특정 시간 동안 실행 된 다음 제어를 다른 RUNNABLE 스레드에 양도합니다 .

예를 들어, 이전 코드 에 t.start () 메서드를 추가 하고 현재 상태에 액세스 해 보겠습니다.

Runnable runnable = new NewState(); Thread t = new Thread(runnable); t.start(); Log.info(t.getState());

이 코드는 출력을 다음과 같이 반환 할 가능성가장 높습니다 .

RUNNABLE

이 예제에서 컨트롤이 t.getState ()에 도달 할 때까지 항상 RUNNABLE 상태 에있을 것이라는 보장은 없습니다 .

Thread-Scheduler에 의해 즉시 예약되어 실행을 마칠 수 있습니다. 이러한 경우 다른 출력을 얻을 수 있습니다.

3.3. 막힌

스레드는 현재 실행할 수없는 경우 BLOCKED 상태입니다. 모니터 잠금을 대기하고 다른 스레드에 의해 잠긴 코드 섹션에 액세스하려고 할 때이 상태로 들어갑니다.

이 상태를 재현 해 보겠습니다.

public class BlockedState { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new DemoThreadB()); Thread t2 = new Thread(new DemoThreadB()); t1.start(); t2.start(); Thread.sleep(1000); Log.info(t2.getState()); System.exit(0); } } class DemoThreadB implements Runnable { @Override public void run() { commonResource(); } public static synchronized void commonResource() { while(true) { // Infinite loop to mimic heavy processing // 't1' won't leave this method // when 't2' try to enter this } } }

이 코드에서 :

  1. 우리는 t1t2 라는 두 개의 다른 스레드를 만들었습니다.
  2. t1 은 동기화 된 commonResource () 메소드를 시작하고 들어갑니다 . 이는 하나의 스레드 만 액세스 할 수 있음을 의미합니다. 이 메소드에 액세스하려는 다른 모든 후속 스레드는 현재 스레드가 처리를 완료 할 때까지 추가 실행에서 차단됩니다.
  3. 경우 T1은 이 방법을 입사이를 반복하면서 무한으로 유지된다; 이것은 다른 모든 스레드가이 메서드에 들어갈 수 없도록 무거운 처리를 모방하는 것입니다.
  4. 이제 t2 를 시작하면 이미 t1에 의해 액세스되고 있는 commonResource () 메서드 로 들어 가려고 시도 하므로 t2BLOCKED 상태 로 유지됩니다.

이 상태에 있으면 t2.getState ()를 호출 하고 다음과 같이 출력을 얻습니다.

BLOCKED

3.4. 기다리는

스레드는 다른 스레드가 특정 작업을 수행하기를 기다리고있을 때 WAITING 상태입니다. JavaDocs에 따르면 모든 스레드는 다음 세 가지 메소드 중 하나를 호출하여이 상태로 들어갈 수 있습니다.

  1. object.wait ()
  2. thread.join () 또는
  3. LockSupport.park ()

참고한다는 점에서 대기 ()가입 () -이 시나리오는 다음 섹션에서 설명 된 바와 같이 우리가 어떤 시간 제한 기간을 정의하지 않습니다.

wait () , notify ()notifyAll () 사용에 대해 자세히 설명하는 별도의 자습서가 있습니다.

지금은이 상태를 재현 해 보겠습니다.

public class WaitingState implements Runnable { public static Thread t1; public static void main(String[] args) { t1 = new Thread(new WaitingState()); t1.start(); } public void run() { Thread t2 = new Thread(new DemoThreadWS()); t2.start(); try { t2.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); Log.error("Thread interrupted", e); } } } class DemoThreadWS implements Runnable { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); Log.error("Thread interrupted", e); } Log.info(WaitingState.t1.getState()); } }

Let's discuss what we're doing here:

  1. We've created and started the t1
  2. t1 creates a t2 and starts it
  3. While the processing of t2 continues, we call t2.join(), this puts t1 in WAITING state until t2 has finished execution
  4. Since t1 is waiting for t2 to complete, we're calling t1.getState() from t2

The output here is, as you'd expect:

WAITING

3.5. Timed Waiting

A thread is in TIMED_WAITING state when it's waiting for another thread to perform a particular action within a stipulated amount of time.

According to JavaDocs, there are five ways to put a thread on TIMED_WAITING state:

  1. thread.sleep(long millis)
  2. wait(int timeout) or wait(int timeout, int nanos)
  3. thread.join(long millis)
  4. LockSupport.parkNanos
  5. LockSupport.parkUntil

To read more about the differences between wait() and sleep() in Java, have a look at this dedicated article here.

For now, let's try to quickly reproduce this state:

public class TimedWaitingState { public static void main(String[] args) throws InterruptedException { DemoThread obj1 = new DemoThread(); Thread t1 = new Thread(obj1); t1.start(); // The following sleep will give enough time for ThreadScheduler // to start processing of thread t1 Thread.sleep(1000); Log.info(t1.getState()); } } class DemoThread implements Runnable { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); Log.error("Thread interrupted", e); } } }

Here, we've created and started a thread t1 which is entered into the sleep state with a timeout period of 5 seconds; the output will be:

TIMED_WAITING

3.6. Terminated

This is the state of a dead thread. It's in the TERMINATED state when it has either finished execution or was terminated abnormally.

We have a dedicated article that discusses different ways of stopping the thread.

Let's try to achieve this state in the following example:

public class TerminatedState implements Runnable { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new TerminatedState()); t1.start(); // The following sleep method will give enough time for // thread t1 to complete Thread.sleep(1000); Log.info(t1.getState()); } @Override public void run() { // No processing in this block } }

Here, while we've started thread t1, the very next statement Thread.sleep(1000) gives enough time for t1 to complete and so this program gives us the output as:

TERMINATED

In addition to the thread state, we can check the isAlive() method to determine if the thread is alive or not. For instance, if we call the isAlive() method on this thread:

Assert.assertFalse(t1.isAlive());

거짓을 반환합니다 . 간단히 말해 스레드는 시작되었고 아직 죽지 않은 경우에만 살아 있습니다.

4. 결론

이 튜토리얼에서 우리는 자바 스레드의 수명주기에 대해 배웠습니다. Thread.State 열거 형으로 정의 된 6 개 상태를 모두 살펴보고 간단한 예제로 재현했습니다.

코드 조각은 거의 모든 컴퓨터에서 동일한 출력을 제공하지만 일부 예외적 인 경우에는 스레드 스케줄러의 정확한 동작을 확인할 수 없기 때문에 일부 다른 출력을 얻을 수 있습니다.

항상 그렇듯이 여기에 사용 된 코드 조각은 GitHub에서 사용할 수 있습니다.