[Java] 스레드 생명 주기 상태

2022. 10. 21. 01:42TIL💡/Java

 

생성 상태(NEW)

생성(NEW)되었지만 시작되지 않은 스레드이다.

각 스레드에 start 메서드가 호출되기 전까지의 상태이다.

 

실행 대기 상태(RUNNABLE)

각 스레드에 start 메서드를 호출하면 스레드가 생성 상태에서 실행 대기(RUNNABLE) 상태로 전환된다.

실행 대기 상태에서의 스레드는 실행 중이거나 실행할 준비가 된 상태이다.

JVM 스레드 스케줄러가 실행에 필요한 리소스와 시간을 할당하기를 기다리는 스레드는 실행할 준비가 되었지만, 아직 실행되지는 않는다.

CPU가 사용할 수 있게 되면 스레드 스케줄러가 스레드를 실행한다.

Runnable runnable1 = () => {}
Thread t1 = new Thread(runnable1);
t1.start();

 

블록 상태(BLOCKED)

동기화 블록이나 I/O 작업을 하는 스레드가 블록 상태에 들어갈 수 있다.

예를 들어 스레드 t1이 다른 스레드 t2에서 이미 접근 중인 동기화된 코드 블록에 들어가려고 하면, t1은 필요한 lock을 얻기 전까지 BLOCKED 상태에 머무른다. lock을 획득한 스레드가 그 클래스에 대한 처리가 끝나면 lock을 해제하는데, 이 때 BLOCKED 상태인 스레드가 있으면 lock을 획득하여 스레드 상태가 RUNNABLE로 바뀌고 그 클래스에 대한 처리를 진행한다.

// 공유 자원
public static class Counter {
    private int count;

    // 이 메서드를 여러 스레드에서 접근하려 할 때,
    // 락을 획득한 스레드만이 접근 가능해지고
    // 다른 스레드는 BLOCKED 상태로 바뀌고 접근하기 위해 대기합니다.
    public synchronized int increase() {
        return ++count;
    }
}

public static void main(String[] args) throws Exception {
    Thread mainThread = Thread.currentThread();
    Counter counter = new Counter();

    Thread thread = new Thread(() -> {
        Thread thisThread = Thread.currentThread();
        while(true) {
            int num = counter.increase();
            System.out.println(
                "otherThread[" +
                mainThread.getName() + ": " + 
                mainThread.getState() + ", " +
                thisThread.getName() + ": " + 
                thisThread.getState() + ", " +
                "value: " + num + "]");
            if(num > 10) 
                break;
        }
    }, "other");

    thread.start();  

    while(true) {
        int num = counter.increase();
        System.out.println(
            "mainThread[" +
            mainThread.getName() + ": " + 
            mainThread.getState() + ", " +
            thread.getName() + ": " + 
            thread.getState() + ", " +
            "value: " + num + "]");

        if(num > 10) 
            break;
    }
}

 

일시정지 상태(WAITING)

스레드 t1에 명시적인 시간 초과 기준을 설정하지 않고 다른 스레드 t2가 작업을 완료하기를 기다릴 때 (WAITING)의 상태이다.

 

🚨 BLOCKED나 WAITING 상태의 차이점

BLOCKED나 WAITING 상태는 모두 스레드 동기화와 관련이 있다.

자바에서 동기화 처리는 모니터 락으로 처리하고 대기 중인 스레드의 상태를 BLOCKED나 WAITING으로 바꿔준다.

BLOCKED는 위에서 설명한 것처럼 공유 자원에 접근할 때, 스레드 한 개씩 접근할 수 있도록 접근된 스레드 이외의 스레드는 BLOCKED 상태로 바꿔주고 대기한다.

 

반면 WAITING는 스레드끼리 동기화가 필요할 때, 대기하는 스레드의 상태를 WAITING으로 바꿔주고 대기가 끝나면 RUNNABLE 상태로 바뀐다. 기본적으로, 자바 Object 객체의 wait(), notify(), notifyAll() 메소드로 구현한다.

 

  • wait(): 갖고 있더 고유락을 해제하고, 스레드를 잠들게 한다.
  • notify(): 잠들어 있던 스레드 중 임의로 하나를 골라 깨운다.
  • notifyAll(): 호출로 잠들어 있던 스레드 모두 깨운다.

세 메서드는 공통으로 반드시 호출 스레드가 대상 객체의 고유 락을 갖고 있어야 한다. 다시 말해, 이 메서드들은 synchronized 블록 내에서 실행되어야 한다. 고유 락을 획득하지 않은 상태에서 위 메서드들 중 하나를 호출하면 IllegalMonitorStateException가 발생한다.

 

Thread.join()은 생성한 스레드가 종료(TERMINATED)될 때까지 WAITING 상태로 대기를 하고 대기가 끝나면 RUNNABLE 상태로 돌아간다.

 

일시 정지 상태(TIMED_WAITING)

스레드 t1이 다른 스레드 t2가 작업을 완료할 때까지 명시적인 시간(일반적으로 밀리초 또는 초 단위로 지정)을 기다릴 때의 상태이다.

Thread.sleep(밀리초)가 호출되면 해당 스레드는 TIMED_WAITING 상태가 된다. 시간이 만료되면 해당 스레드는 다시 RUNNABLE 상태가 된다.

 

종료 상태(TERMINATED)

비정상적으로 중단되거나 성공적으로 작업을 완료한 자바 스레드 상태이다.

TERMINATED 상태가 되는 경우는 두 가지이다.

  • stop() 메서드를 호출하여 강제 종료 → 이는 스레드를 강제적으로 종료하기 때문에 자원들이 불안전한 상황에 놓일 수 있어서 deprecated 되었다.
  • interrupt() 메서드 호출 → 스레드가 실행 중일 때는 영향이 없고 대기 상태가 되었을 때 InterruptedException 예외 발생

참고

- https://cheetile.tistory.com/entry/JAVA-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%83%81%ED%83%9C-Thread-%ED%81%B4%EB%9E%98%EC%8A%A4