00:00

Thread Life Cycle in Java – Thread States and Transitions

Every Java thread passes through a series of states from the moment it is created until it finishes. Understanding this life cycle helps you reason about what a thread is doing, read thread dumps, and diagnose problems such as deadlocks and threads that never finish.

Java defines six states in the Thread.State enum: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING and TERMINATED. This tutorial explains each one and the transitions between them, and shows every state in a runnable program.

1. Thread States at a Glance

Life cycle of a thread in Java showing the thread states
StateMeaningEntered by
NEWCreated but not startednew Thread(...)
RUNNABLERunning, or ready to run and waiting for CPU timestart(); leaving any waiting state
BLOCKEDWaiting to acquire a monitor lockEntering a synchronized block or method held by another thread
WAITINGWaiting indefinitely for another threadwait(), join(), LockSupport.park()
TIMED_WAITINGWaiting for at most a given timesleep(ms), wait(ms), join(ms), parkNanos
TERMINATEDFinishedrun() returns or throws an exception

2. The Six States in Detail

1. NEW

A Thread object exists, but no operating-system thread has been created yet. The only useful action is start().

2. RUNNABLE

After start(), the thread is eligible to run. Java does not distinguish "ready" from "running": both are RUNNABLE, and the OS scheduler decides which runnable thread uses a CPU core. A thread blocked in native I/O, such as reading a socket, is also reported as RUNNABLE.

3. BLOCKED

The thread wants to enter a synchronized block or method, but another thread holds the monitor. It becomes RUNNABLE again when it acquires the lock. (Threads waiting on java.util.concurrent locks appear as WAITING instead.)

4. WAITING

The thread waits with no time limit for another thread to act: a notify()/notifyAll() after wait(), the end of a thread it join()ed, or an unpark().

5. TIMED_WAITING

Like WAITING, but it returns to RUNNABLE automatically when the timeout expires. Thread.sleep() is the most common cause.

6. TERMINATED

The run() method has completed, normally or with an uncaught exception. A terminated thread cannot be restarted.

3. Observing Every State

getState() returns a thread's current state. This program drives threads through all six states:

ThreadStates.java
public class ThreadStates {
    static final Object lock = new Object();

    static void waitUntil(Thread t, Thread.State s) throws InterruptedException {
        while (t.getState() != s) Thread.sleep(5);
    }

    public static void main(String[] args) throws InterruptedException {
        // NEW -> RUNNABLE -> TIMED_WAITING -> TERMINATED
        Thread sleeper = new Thread(() -> {
            long end = System.nanoTime() + 100_000_000;
            while (System.nanoTime() < end) { }               // busy for 100 ms: RUNNABLE
            try { Thread.sleep(300); } catch (InterruptedException e) { }   // TIMED_WAITING
        });
        System.out.println("After new:      " + sleeper.getState());
        sleeper.start();
        System.out.println("After start():  " + sleeper.getState());
        waitUntil(sleeper, Thread.State.TIMED_WAITING);
        System.out.println("In sleep():     " + sleeper.getState());
        sleeper.join();
        System.out.println("After finish:   " + sleeper.getState());

        // BLOCKED: waiting to enter a synchronized block held by main
        Thread blocked = new Thread(() -> { synchronized (lock) { } });
        synchronized (lock) {
            blocked.start();
            waitUntil(blocked, Thread.State.BLOCKED);
            System.out.println("Lock held:      " + blocked.getState());
        }
        blocked.join();

        // WAITING: wait() with no timeout until notify()
        Thread waiter = new Thread(() -> {
            synchronized (lock) {
                try { lock.wait(); } catch (InterruptedException e) { }
            }
        });
        waiter.start();
        waitUntil(waiter, Thread.State.WAITING);
        System.out.println("In wait():      " + waiter.getState());
        synchronized (lock) { lock.notify(); }
        waiter.join();
        System.out.println("After notify(): " + waiter.getState());
    }
}
Output
After new:      NEW
After start():  RUNNABLE
In sleep():     TIMED_WAITING
After finish:   TERMINATED
Lock held:      BLOCKED
In wait():      WAITING
After notify(): TERMINATED

getState() is designed for monitoring and debugging, not for synchronization; the state may change immediately after you read it.

4. A Thread Cannot Be Started Twice

StartTwice.java
public class StartTwice {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> System.out.println("Running once"));
        t.start();
        t.join();
        try {
            t.start();                       // a TERMINATED thread cannot be restarted
        } catch (IllegalThreadStateException e) {
            System.out.println("Second start(): " + e.getClass().getSimpleName());
        }
    }
}
Output
Running once
Second start(): IllegalThreadStateException

To run the same work again, create a new Thread, or better, submit the task to a thread pool.

5. Interrupting a Waiting Thread

interrupt() asks a thread to stop what it is doing. A thread in sleep, wait or join wakes up with an InterruptedException; a running thread can check Thread.currentThread().isInterrupted():

InterruptDemo.java
public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            try {
                System.out.println("Worker: sleeping for 10 seconds");
                Thread.sleep(10_000);
                System.out.println("Worker: woke up normally");
            } catch (InterruptedException e) {
                System.out.println("Worker: interrupted, cleaning up and exiting");
            }
        });
        worker.start();
        Thread.sleep(200);
        worker.interrupt();          // TIMED_WAITING -> RUNNABLE (via InterruptedException)
        worker.join();
        System.out.println("Main: worker state = " + worker.getState());
    }
}
Output
Worker: sleeping for 10 seconds
Worker: interrupted, cleaning up and exiting
Main: worker state = TERMINATED

6. Methods That Change State

MethodTransition
start()NEW → RUNNABLE
Thread.sleep(ms)RUNNABLE → TIMED_WAITING → RUNNABLE
wait() / notify()RUNNABLE → WAITING → BLOCKED (reacquiring the lock) → RUNNABLE
join()RUNNABLE → WAITING until the other thread terminates
synchronized entryRUNNABLE → BLOCKED while the lock is busy
Thread.yield()Stays RUNNABLE; only hints that others may run
interrupt()Wakes a WAITING / TIMED_WAITING thread with an exception

The old methods stop(), suspend() and resume() are deprecated and unsafe (and stop() now always throws). Use interruption or a volatile flag instead.

7. Summary

  • A Java thread is in one of six states: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING or TERMINATED.
  • RUNNABLE covers both running and ready-to-run.
  • BLOCKED is about monitor locks; WAITING and TIMED_WAITING are about waiting for other threads or time.
  • A terminated thread cannot be restarted; use interrupt() to request a stop.

Related Tutorials