Understanding Java Thread States and Transitions

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 RUNNABLE may 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

  1. BLOCKED vs WAITING/TIMED_WAITING: BLOCKED relates specifically to contention over synchronized locks; WAITING and TIMED_WAITING are caused by explicit wait calls.
  2. Lock Release Behavior: sleep() does not release locks, whereas wait() releases the associated monitor immediately.
  3. Effect of Interrupting Threads: Calling interrupt() on a WAITING or TIMED_WAITING thread throws InterruptedException and returns it to RUNNABLE.

Summary

  • Java threads have six distinct states defined by Thread.State.
  • Transitions occur based on lifecycle events and synchronization behavior.
  • BLOCKED is exclusive to synchronized lock contention.
  • sleep() retains locks, while wait() releases them.

Tags: java thread Concurrency state-machine multithreading

Posted on Sun, 14 Jun 2026 16:33:51 +0000 by jamesl73