티스토리 뷰

언어/Java

Java Thread 상태 종류

snvlqkq 2026. 4. 21. 11:13

Java Thread는 Thread.State enum으로 정의된 6가지 상태를 가진다.

1. NEW

Thread 객체가 생성만 된 상태. 아직 스레드가 실행 중이 아님.
명시적으로 start()를 호출해야 스레드가 실행된다.

Thread t = new Thread(() -> {}); // NEW

스레드 실행

Thread t = new Thread(() -> {});
t.start();

2. RUNNABLE

start()가 호출되어 실행 가능한 상태. JVM 레벨에서는 실행 중이지만, OS 레벨에서는 CPU를 할당받아 실제 실행 중이거나 실행 대기 중일 수 있다. Java는 이 둘을 구분하지 않고 RUNNABLE로 통합해서 표현한다.

"실행 가능"이라는 표현을 쓰는 이유

OS 레벨에서는 Thread 상태가 더 세분화되어 있다.

  • Running: 현재 CPU 코어에서 실제로 명령어를 실행 중
  • Ready (Runnable): 실행할 준비는 끝났지만 CPU를 할당받지 못해 대기 큐에 있음

CPU 코어 수는 제한적인데 Thread는 수백, 수천 개일 수 있어 OS는 타임 슬라이스[각주:1] 단위로 Thread들을 번갈아 CPU에 올렸다 내렸다 한다. 이 과정에서 Thread는 Running ↔ Ready 상태를 끊임없이 오간다.

JVM은 이 두 상태를 모두 RUNNABLE 하나로 통합한다.

3. BLOCKED

락을 획득하기 위해 대기하는 상태. synchronized 블록/메서드에 진입하려는데 다른 Thread가 이미 락을 잡고 있을 때의 상태이다.

synchronized(lock) { ... } // 락 대기 시 BLOCKED

여기서 "락"은 정확히는 모니터(Monitor) 락을 가리킨다. 그래서 BLOCKED가 뭔지 제대로 이해하려면 모니터 구조부터 짚고 가야 한다.

모니터(Monitor)란

모니터는 "한 번에 하나의 Thread만 접근할 수 있도록 보장하는 통제하는 장치이다." 락(Lock) + 조건 변수(Condition Variable) 를 하나로 묶어 객체에 내장한 형태로, Java는 이 모니터를 모든 객체에 내장하는 방식으로 구현했다. 이걸 intrinsic lock 또는 monitor lock이라고 부른다.

모니터 락의 구조

JVM 레벨에서 모니터는 세 부분으로 구성된다.

  1. Owner: 현재 락을 소유한 Thread
  2. Entry Set (진입 큐): 락을 획득하려고 대기 중인 Thread들 → BLOCKED 상태
  3. Wait Set (대기 큐): wait()를 호출해서 조건을 기다리는 Thread들 → WAITING 상태

BLOCKED는 Entry Set에 들어가 있는 상태고, Wait Set에 들어간 Thread는 WAITING으로 따로 구분된다.

ReentrantLock은 BLOCKED가 아니다

ReentrantLock모니터 락이 아니다. 별도의 Lock 구현체이고, 내부적으로 AQS(AbstractQueuedSynchronizer) + LockSupport.park()를 쓴다. 그래서 ReentrantLock.lock()으로 락을 기다리는 Thread는 BLOCKED가 아니라 WAITING 상태로 표시된다.

Thread dump 분석할 때 이 차이를 놓치면 락 경합의 원인을 엉뚱한 데서 찾게 된다.

4. WAITING

다른 Thread의 특정 작업이 완료될 때까지 무기한 대기하는 상태. 명시적으로 깨워야 RUNNABLE로 돌아온다.

5. TIMED_WAITING

지정된 시간 동안만 대기하는 상태. 시간이 지나면 자동으로 RUNNABLE로 전환된다.

6. TERMINATED

run() 메서드 실행이 완료되었거나 예외로 종료된 상태. 한 번 TERMINATED가 되면 다시 시작할 수 없다.

실무에서 자주 헷갈리는 포인트

BLOCKED vs WAITING

BLOCKED는 synchronized 락 경합에서만 발생하고, ReentrantLock.lock()에서 락을 기다릴 때는 내부적으로 LockSupport.park()를 쓰기 때문에 WAITING 상태가 된다. Thread dump를 분석할 때 이 차이를 모르면 락 경합을 놓치기 쉽다.

RUNNABLE인데 실제로는 I/O 대기

소켓 read, 파일 I/O 같은 블로킹 I/O를 수행 중인 Thread는 JVM에서 RUNNABLE로 표시된다. Thread dump에서 RUNNABLE이라고 해서 반드시 CPU를 쓰고 있는 건 아니라는 점이 TPS 병목 분석할 때 중요하다.

상태 전이는 단방향이 아님

RUNNABLE ↔ BLOCKED/WAITING/TIMED_WAITING 사이를 여러 번 오갈 수 있지만, NEW → RUNNABLE과 * → TERMINATED는 단방향이다.

  1. OS 스케줄러가 하나의 Thread에게 CPU를 점유하도록 허용하는 시간의 단위 [본문으로]
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/05   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함