A Java thread is always in exactly one of six states defined by the Thread.State enum: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED. You query the current one with thread.getState().
The six states
- NEW — created but
start()not yet called. - RUNNABLE — eligible to run; either running on a core or waiting for the scheduler. Java does not distinguish "running" from "ready to run" — both are RUNNABLE. A thread blocked in an OS-level read (e.g. socket I/O) also shows as RUNNABLE.
- BLOCKED — waiting to acquire a monitor lock to enter or re-enter a
synchronizedblock. Only contention on intrinsic locks produces this state. - WAITING — parked indefinitely via
Object.wait(),Thread.join(), orLockSupport.park(), until another thread signals it. - TIMED_WAITING — like WAITING but with a deadline:
sleep(ms),wait(ms),join(ms),LockSupport.parkNanos. - TERMINATED —
run()has returned or thrown.
How a thread moves between them
NEW | start() v RUNNABLE <-------------------------------+ | synchronized (lock held by other) | lock acquired v | BLOCKED -----------------------------------+ | wait()/join()/park() notify()/notifyAll()/unpark()/join target ends v | WAITING -----------------------------------+ | sleep(t)/wait(t)/join(t) timeout elapses v | TIMED_WAITING -----------------------------+ | | run() returns or throws v TERMINATED
Object lock = new Object();
Thread t = new Thread(() -> {
synchronized (lock) {
try { lock.wait(); } catch (InterruptedException e) {}
}
});
System.out.println(t.getState()); // NEW
t.start();
Thread.sleep(50);
System.out.println(t.getState()); // WAITING (parked in wait())
Common confusion
BLOCKED is specifically about intrinsic-lock contention. A thread waiting on a ReentrantLock shows as WAITING (it uses LockSupport.park under AQS), not BLOCKED. And a thread doing blocking I/O is RUNNABLE, which surprises people reading thread dumps.