Backend Development 14 min read

Understanding Java Thread States and Their Transitions

This article explains Java thread states, their lifecycle transitions, and provides practical code examples demonstrating NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED states, along with insights on synchronization, spurious wakeups, and best practices for using wait() within loops.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Understanding Java Thread States and Their Transitions

Why Understand Java Thread States

Threads are the smallest unit of execution in the JVM; understanding their state transitions is the foundation for mastering multithreading problems.

Java Thread State Transition Diagram

Below is a diagram that shows how a thread can move between the six states defined by the Thread.State enum.

What Are the Java Thread States?

In the JVM a thread can be in one of six states: NEW , RUNNABLE , BLOCKED , WAITING , TIMED_WAITING , or TERMINATED . These correspond to the constants defined in the Thread.State enumeration.

Thread.State enum source (comments removed for readability):

public enum State {
  NEW,
  RUNNABLE,
  BLOCKED,
  WAITING,
  TIMED_WAITING,
  TERMINATED;
}

At any given moment a thread can be in exactly one of these states. The states are virtual machine concepts and do not directly reflect operating‑system thread states.

NEW and TERMINATED

When a thread object is created but start() has not been called, the thread is in the NEW state. After the thread’s run() method completes, it moves to TERMINATED .

RUNNABLE

Calling start() puts the thread into the RUNNABLE state, meaning it is eligible to run on the CPU.

/**
 * Program purpose: Observe various thread states
 * created at 2020-06-26 19:09
 */
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.printf("%s thread running\n", Thread.currentThread().getName());
    }
}

public class ThreadStateDemo {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        System.out.printf("After creation, state: %s\n", myThread.getState());
        myThread.start();
        System.out.printf("After start(), state: %s\n", myThread.getState());
        // Sleep 50 ms to let MyThread finish
        Thread.sleep(50);
        System.out.printf("After join, state: %s\n", myThread.getState());
    }
}

Output:

创建线程后,线程的状态为:NEW
调用start()方法后线程的状态为:RUNNABLE
Thread-0线程运行
再次打印线程的状态为:TERMINATED

BLOCKED

If a running thread attempts to enter a synchronized block or method and cannot acquire the monitor, it enters the BLOCKED state until the lock becomes available.

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ThreadBlockedStateDemo {
    public static void main(String[] args) {
        Thread threadA = new Thread(() -> method01(), "A-Thread");
        Thread threadB = new Thread(() -> method01(), "B-Thread");
        threadA.start();
        threadB.start();
        log.info("Thread A state: {}", threadA.getState());
        log.info("Thread B state: {}", threadB.getState());
    }

    public static synchronized void method01() {
        log.info("[{}]: start method", Thread.currentThread().getName());
        try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
        log.info("[{}]: end method", Thread.currentThread().getName());
    }
}

Sample log shows thread A acquiring the lock (RUNNABLE) while thread B is BLOCKED, then B proceeds after A releases the lock.

WAITING and TIMED_WAITING

Threads can enter waiting states when they call methods such as Object.wait() , Thread.join() , or LockSupport.park() . WAITING has no timeout, while TIMED_WAITING includes a timeout.

Official documentation excerpt:

A thread in the waiting state is waiting for another thread to perform a particular action. For example, a thread that has called Object.wait() is waiting for another thread to call Object.notify() or Object.notifyAll() on that object.

Example demonstrating the WAITING state using a ticket‑selling scenario:

public class ThreadWaitingStateDemo {
    public static void main(String[] args) throws InterruptedException {
        Ticket ticket = new Ticket();
        Thread fanA = new Thread(() -> {
            synchronized (ticket) {
                while (ticket.getNum() == 0) {
                    try { ticket.wait(); } catch (InterruptedException e) { e.printStackTrace(); }
                }
                ticket.buy();
            }
        }, "Fan A");
        Thread fanB = new Thread(() -> {
            synchronized (ticket) {
                while (ticket.getNum() == 0) {
                    try { ticket.wait(); } catch (InterruptedException e) { e.printStackTrace(); }
                }
                ticket.buy();
            }
        }, "Fan B");
        fanA.start();
        fanB.start();
        Thread.sleep(10); // ensure both fans are waiting
        log.info("Fan A state: {}", fanA.getState());
        log.info("Fan B state: {}", fanB.getState());
        // Ticket supplier adds tickets and notifies all waiting fans
        new Thread(() -> {
            synchronized (ticket) {
                if (ticket.getNum() == 0) {
                    ticket.addTicket();
                    ticket.notifyAll();
                }
            }
        }, "Supplier").start();
    }
}

@Slf4j
class Ticket {
    private int num = 0;
    public int getNum() { return num; }
    public void addTicket() {
        try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
        log.info("Adding tickets");
        this.num += 2;
    }
    public void buy() {
        log.info("[{}]: bought a ticket", Thread.currentThread().getName());
        log.info("[{}]: leaving the hall", Thread.currentThread().getName());
    }
}

When ticket.wait() is replaced with ticket.wait(10) , the threads move to TIMED_WAITING instead of WAITING .

Why wait() Should Be Inside a while Loop

Using if instead of while can cause a thread to proceed after a spurious wake‑up even though the condition (e.g., tickets available) is still false. The Java documentation explicitly recommends wrapping wait() in a loop to guard against spurious wake‑ups and interruptions.

Difference Between BLOCKED and WAITING

A thread in BLOCKED is actively competing for a monitor lock; once the CPU schedules it and the lock is free, it proceeds. A thread in WAITING does not compete for the lock; it remains dormant until another thread notifies it or a timeout expires.

Both states are part of the thread lifecycle, but they represent different waiting strategies.

---

Thank you for reading; hope it helped you understand Java thread states.

Source: blog.csdn.net/limenghua9112/article/details/106975105

JavaconcurrencythreadSynchronizationThreadState
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.