Back to Thread Basics & Overview
Q3Question 3 of 7

What are the thread lifecycle states?

Key Concepts
  • Java defines six thread states in the Thread.State enum: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED.
  • NEW means the Thread object exists but start() has not been called yet.
  • RUNNABLE covers both running on the CPU and ready-to-run in the OS scheduler queue.
  • BLOCKED means the thread is waiting to acquire a monitor lock held by another thread.
  • WAITING and TIMED_WAITING are voluntary waits triggered by wait(), join(), sleep(), or park().
  • TERMINATED is permanent: calling start() on a terminated thread throws IllegalThreadStateException.

Definition

A Java thread moves through six states: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED, and a terminated thread cannot be restarted.
  • NEW – the Thread object has been created but start() has not been called yet.
  • RUNNABLE – the thread is eligible to run; it may be executing on a CPU core or waiting in the OS scheduler queue for its turn.
  • BLOCKED – the thread is waiting to acquire a monitor lock that another thread currently holds (trying to enter a synchronized block).
  • WAITING – the thread has voluntarily paused and will not resume until another thread sends a specific signal: notify(), notifyAll(), unpark(), or the joined thread terminates.
  • TIMED_WAITING – the thread has voluntarily paused with a timeout; it resumes either when signaled or when the timeout expires.
  • TERMINATED – the run() method has completed, either normally or by throwing an uncaught exception. The thread cannot be restarted.

Think of a bank teller's workday. NEW is the teller who has been hired but has not shown up for the first day yet. RUNNABLE is the teller at the counter, either serving a customer or waiting for the next one in the queue. BLOCKED is the teller standing outside the vault door because another teller is inside. WAITING is the teller sitting in the break room until the manager calls. TIMED_WAITING is the teller on a 30-minute lunch break who comes back automatically. TERMINATED is the teller who has retired and cannot be un-retired.

Understanding the Concepts

What are the six thread states?

The Thread.State enum defines exactly six states. Java does not distinguish between "running" and "ready to run" at the language level: both are RUNNABLE.

JAVA
package com.bank.core;

public class ThreadStateDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try { Thread.sleep(1000); }
            catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        }, "BankWorker");

        System.out.println("After creation: " + t.getState());   // NEW
        t.start();
        System.out.println("After start:    " + t.getState());   // RUNNABLE
        Thread.sleep(100);
        System.out.println("During sleep:   " + t.getState());   // TIMED_WAITING
        t.join();
        System.out.println("After join:     " + t.getState());   // TERMINATED
    }
}
TEXT
After creation: NEW
After start:    RUNNABLE
During sleep:   TIMED_WAITING
After join:     TERMINATED

How do state transitions work?

The transitions follow strict rules. A thread can only move forward through the lifecycle. It never goes from TERMINATED back to NEW.

  • start() moves the thread from NEW to RUNNABLE.
  • Entering a synchronized block when the lock is held moves the thread to BLOCKED.
  • wait(), join(), or LockSupport.park() moves the thread to WAITING.
  • sleep(ms), wait(ms), or join(ms) moves the thread to TIMED_WAITING.
  • Completing run() or throwing an uncaught exception moves the thread to TERMINATED.

How do you observe each state in code?

JAVA
package com.bank.core;

public class AllStatesDemo {
    private static final Object vaultLock = new Object();

    public static void main(String[] args) throws InterruptedException {
        // BLOCKED state: t2 tries to enter synchronized held by t1
        Thread t1 = new Thread(() -> {
            synchronized (vaultLock) {
                try { Thread.sleep(3000); }
                catch (InterruptedException e) { Thread.currentThread().interrupt(); }
            }
        }, "VaultHolder");

        Thread t2 = new Thread(() -> {
            synchronized (vaultLock) {
                System.out.println("t2 acquired vault lock");
            }
        }, "VaultWaiter");

        t1.start();
        Thread.sleep(100);
        t2.start();
        Thread.sleep(100);
        System.out.println("t2 state: " + t2.getState()); // BLOCKED

        // WAITING state: t3 calls wait()
        Thread t3 = new Thread(() -> {
            synchronized (vaultLock) {
                try { vaultLock.wait(); }
                catch (InterruptedException e) { Thread.currentThread().interrupt(); }
            }
        }, "Waiter");

        t1.join();
        t3.start();
        Thread.sleep(100);
        System.out.println("t3 state: " + t3.getState()); // WAITING

        // Wake t3 so the program can exit
        synchronized (vaultLock) { vaultLock.notifyAll(); }
        t2.join();
        t3.join();
    }
}
TEXT
t2 state: BLOCKED
t2 acquired vault lock
t3 state: WAITING

Why can you not restart a terminated thread?

Once a thread reaches the TERMINATED state, calling start() throws IllegalThreadStateException. The internal native resources have been released. To run the same task again, create a new Thread object.

JAVA
package com.bank.core;

public class RestartDemo {
    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> System.out.println("Processing trade");
        Thread t = new Thread(task);
        t.start();
        t.join();

        try {
            t.start(); // IllegalThreadStateException!
        } catch (IllegalThreadStateException e) {
            System.out.println("Cannot restart: " + e.getMessage());
        }

        // Create a new thread with the same Runnable
        Thread t2 = new Thread(task);
        t2.start();
        t2.join();
    }
}
TEXT
Processing trade
Cannot restart: null
Processing trade

How is RUNNABLE different from actually running?

Java merges "running on a CPU core" and "ready to run but waiting for CPU time" into the single RUNNABLE state. The OS scheduler decides which RUNNABLE threads actually get CPU time. On a machine with 4 cores, at most 4 threads can truly execute simultaneously. The rest sit in the scheduler's ready queue but are still reported as RUNNABLE.

How do you use thread states for monitoring?

In production banking systems, monitoring thread states reveals performance bottlenecks.

  • Many BLOCKED threads on the same lock indicate lock contention. The fix is to reduce the synchronized scope, use ReadWriteLock, or switch to lock-free structures.
  • Many TIMED_WAITING threads are normal during low traffic: pool workers are idle.
  • Zero RUNNABLE threads with a full task queue indicates a deadlock. Take a thread dump immediately with jstack.
JAVA
package com.bank.core;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

public class ThreadMonitor {
    public static void printStateSummary() {
        ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
        ThreadInfo[] infos = tmx.dumpAllThreads(false, false);
        long blocked = 0, waiting = 0, runnable = 0;
        for (ThreadInfo info : infos) {
            switch (info.getThreadState()) {
                case BLOCKED -> blocked++;
                case WAITING, TIMED_WAITING -> waiting++;
                case RUNNABLE -> runnable++;
                default -> { }
            }
        }
        System.out.println("RUNNABLE=" + runnable
            + " BLOCKED=" + blocked + " WAITING=" + waiting);
    }
}

What should a senior engineer know?

  • Thread dumps: jstack or kill -3 on Linux produces a thread dump showing every thread's state and stack trace. This is the primary tool for diagnosing deadlocks and contention.
  • RUNNABLE does not mean CPU-busy: a thread doing a blocking Socket.read() appears as RUNNABLE because the JVM does not distinguish between CPU work and native IO waits.
  • Virtual thread states (Java 21): virtual threads also use the same Thread.State enum, but a virtual thread in WAITING does not consume an OS thread. It is unmounted from the carrier thread.

Visual Overview

DIAGRAM
                         start()
            NEW ──────────────────▶ RUNNABLE
                                   │    ▲
                     ┌─────────────┘    │
                     │                  │ lock acquired
                     ▼                  │
              trying to enter     ┌─────┴─────┐
              synchronized ──────▶│  BLOCKED   │
                                  └───────────┘

            RUNNABLE
               │    ▲
               │    │ notify() / unpark() / join completes
               ▼    │
          ┌────────────────┐
          │    WAITING      │  wait(), join(), park()
          └────────────────┘

            RUNNABLE
               │    ▲
               │    │ timeout expires / notify()
               ▼    │
          ┌────────────────┐
          │ TIMED_WAITING   │  sleep(ms), wait(ms), join(ms)
          └────────────────┘

            RUNNABLE ──── run() ends ────▶ TERMINATED
                                          (permanent)

Key Takeaway

A Java thread passes through six states defined by the Thread.State enum. It starts as NEW, becomes RUNNABLE when start() is called, and ends as TERMINATED when run() completes. Between RUNNABLE and TERMINATED, the thread may temporarily enter BLOCKED (involuntary wait for a monitor lock), WAITING (voluntary indefinite wait for a signal), or TIMED_WAITING (voluntary wait with a timeout).

The most common confusion is between BLOCKED and WAITING. A BLOCKED thread is stuck at a synchronized entry point because another thread holds the lock. A WAITING thread chose to pause by calling wait(), join(), or park(). BLOCKED resolves automatically when the lock is released. WAITING requires an explicit signal from another thread.

Monitoring thread states in production is essential. A spike in BLOCKED threads signals lock contention. Many TIMED_WAITING threads during low traffic is normal. No RUNNABLE threads with a full queue means deadlock.

Remember one sentence: Java threads move through NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED, and once terminated, a thread cannot be restarted.

---

Share
Use ← → arrow keys to navigate