Overview of Thread States in Java
Thread states form the foundation of concurrent programming in Java. The JDK defines six primary thread states through the java.lang.Thread.State enum. These states reflect different phases of a thread's lifecycle, influenced by synchronization and blocking behaviors.
Six Core Thread States
The official Thread.State enum specifies all valid thread states:
| State Name | Description |
|---|---|
NEW |
A thread has been instantiated but not yet started via start(). |
RUNNABLE |
The thread has been started and can either be running or waiting for CPU time. |
BLOCKED |
The thread is blocked waiting to acquire a monitor lock (e.g., in a synchronized block). |
WAITING |
The thread is waiting indefinitely until another thread performs an action (e.g., wait() or join()). |
TIMED_WAITING |
The thread is waiting for a specified amount of time (e.g., sleep() or timed wait()). |
TERMINATED |
The thread has completed execution or terminated due to an uncaught exception. |
Note: While
RUNNABLEmay imply two sub-states—ready and running—the JDK treats them uniformly under one state.
State Transition Rules
Thread transitions are primarily unidirectional, with some bidirectional exceptions. Below is a breakdown of key transitions.
1. From NEW to RUNNABLE
Only calling start() moves a thread from NEW to RUNNABLE. Invoking start() more than once results in IllegalThreadStateException.
Thread task = new Thread(() -> System.out.println("Executing"));
System.out.println(task.getState()); // NEW
task.start();
System.out.println(task.getState()); // RUNNABLE (may vary slightly due to scheduling)
2. Between RUNNABLE and BLOCKED
A thread enters BLOCKED when it fails to acquire a synchronized lock. It returns to RUNNABLE upon acquiring the lock.
Object monitor = new Object();
Thread first = new Thread(() -> {
synchronized (monitor) {
try {
Thread.sleep(5000);
} catch (InterruptedException ignored) {}
}
});
Thread second = new Thread(() -> {
synchronized (monitor) {
System.out.println("Acquired lock");
}
});
first.start();
Thread.sleep(100);
second.start();
Thread.sleep(100);
System.out.println(first.getState()); // TIMED_WAITING
System.out.println(second.getState()); // BLOCKED
3. From RUNNABLE to WAITING
Transition occurs when a thread invokes methods like wait(), join(), or LockSupport.park().
Object lock = new Object();
Thread waiter = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("Waiting...");
lock.wait();
System.out.println("Resumed");
} catch (InterruptedException ignored) {}
}
});
waiter.start();
Thread.sleep(100);
System.out.println(waiter.getState()); // WAITING
synchronized (lock) {
lock.notify();
}
4. From RUNNABLE to TIMED_WAITING
Occurs when using time-limited operations such as sleep(), wait(long), or join(long).
Thread sleeper = new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException ignored) {}
});
sleeper.start();
Thread.sleep(100);
System.out.println(sleeper.getState()); // TIMED_WAITING
5. Transition to TERMINATED
A thread reaches TERMINATED after completing its run() method or throwing an uncaught exception.
Thread executor = new Thread(() -> {
System.out.println("Running");
});
executor.start();
Thread.sleep(1000);
System.out.println(executor.getState()); // TERMINATED
Clarifications
- BLOCKED vs WAITING/TIMED_WAITING:
BLOCKEDrelates specifically to contention oversynchronizedlocks;WAITINGandTIMED_WAITINGare caused by explicit wait calls. - Lock Release Behavior:
sleep()does not release locks, whereaswait()releases the associated monitor immediately. - Effect of Interrupting Threads: Calling
interrupt()on aWAITINGorTIMED_WAITINGthread throwsInterruptedExceptionand returns it toRUNNABLE.
Summary
- Java threads have six distinct states defined by
Thread.State. - Transitions occur based on lifecycle events and synchronization behavior.
BLOCKEDis exclusive tosynchronizedlock contention.sleep()retains locks, whilewait()releases them.