What is the difference between the BLOCKED and WAITING thread states, and what transitions a thread into each?
answer
- BLOCKED = wants a monitor lock (involuntary)
- WAITING = called wait/join/park, no timeout (voluntary)
- wait() releases the monitor; BLOCKED is trying to get one
- After notify: WAITING -> BLOCKED (re-acquire) -> RUNNABLE
- Dump: many BLOCKED = contention/deadlock; many WAITING = idle pool
basics
~20 sBLOCKED means a thread is waiting to get a lock so it can enter a synchronized block. WAITING means a thread chose to wait for another thread to signal it, by calling wait(), join(), or park() with no timeout.
solid answer
~40 sBLOCKED and WAITING are both 'not running', but for different reasons. A thread is BLOCKED when it tries to enter a synchronized block or method but another thread holds that object's monitor lock; it stays BLOCKED until the lock frees and it acquires it. That's involuntary contention. A thread is WAITING when it voluntarily suspends itself to wait for a condition or another thread's action — via Object.wait(), Thread.join(), or LockSupport.park(), all with no timeout. It leaves WAITING only on notify()/notifyAll(), the joined thread finishing, or unpark(). A subtle but important detail: a thread returning from wait() must re-acquire the monitor it released, so it transitions WAITING → BLOCKED (re-acquiring) → RUNNABLE. In thread dumps, lots of BLOCKED threads suggest lock contention; many WAITING threads usually mean an idle pool waiting on a condition.
code
java · 14 linesObject lock = new Object();
// Thread A: holds the monitor and waits -> WAITING (releases the monitor)
synchronized (lock) {
while (!ready) {
lock.wait(); // -> WAITING; monitor released here
} // on wakeup: re-acquires monitor (may pass via BLOCKED)
}
// Thread B: tries to enter the same synchronized block while A holds it -> BLOCKED
synchronized (lock) { // -> BLOCKED if the monitor is held
ready = true;
lock.notifyAll(); // wakes A; A still must re-acquire 'lock'
}go deeper
Knows BLOCKED is about waiting for a lock and WAITING is about wait()/join(), even if the monitor re-acquisition detail is fuzzy.
Cleanly separates involuntary lock contention (BLOCKED) from voluntary suspension (WAITING) and names the triggering calls for each.
Explains that wait() releases the monitor and that a notified thread re-acquires it (WAITING→BLOCKED→RUNNABLE), and reads thread dumps to distinguish contention from idle waits.
Uses BLOCKED/WAITING patterns in dumps to diagnose contention vs deadlock vs lost-notify, and steers designs away from hot synchronized sections toward j.u.c primitives.
## Background terms - A **monitor lock** (a.k.a. intrinsic lock): every Java object has one. Entering a `synchronized(obj){...}` block acquires `obj`'s monitor; leaving it releases the monitor. Only one thread can hold a given monitor at a time. - **`Object.wait()`**: called *while holding* an object's monitor, it **atomically releases that monitor and suspends** the calling thread until another thread calls `notify()`/`notifyAll()` on the same object. It must be called inside `synchronized` on that object. - **`Thread.join()`**: makes the caller wait until the target thread terminates. - **`LockSupport.park()`**: a low-level primitive that suspends the current thread until `unpark()` is called on it; used by `java.util.concurrent` locks. ## BLOCKED — involuntary monitor contention A thread is **BLOCKED** in exactly one situation: it is trying to **enter a `synchronized` block/method** (or re-enter one after `wait()`), and another thread currently holds that object's monitor. The thread cannot proceed and cannot do anything else; it sits in BLOCKED until the lock is released and the scheduler grants the monitor to it. This is *contention you didn't ask for* — you just wanted the lock, and someone else had it. Entering: attempt to acquire a contended monitor. Leaving: the holder releases the monitor and this thread wins it → RUNNABLE. ## WAITING — voluntary, indefinite suspension A thread is **WAITING** when it deliberately calls one of these *with no timeout*: - `Object.wait()` — wait for a `notify`/`notifyAll` on that object, - `Thread.join()` — wait for another thread to die, - `LockSupport.park()` — wait for an `unpark`. The thread has chosen to give up the CPU until some *other* thread takes an action. Crucially, `wait()` **releases the monitor** while waiting (unlike BLOCKED, where the thread holds no relevant lock and is trying to *get* one). Entering: call `wait()`/`join()`/`park()` (no timeout). Leaving: `notify`/`notifyAll` (for wait), target thread terminates (for join), or `unpark` (for park). ## The transition that trips people up When a waiting thread is notified, it does **not** jump straight to RUNNABLE. Because `wait()` released the monitor, the thread must **re-acquire** that monitor before it can continue executing the `synchronized` block. If another thread holds the monitor at that moment, the awakened thread becomes **BLOCKED** until it can re-acquire it. So the real path is: `WAITING → (notified) → BLOCKED (re-acquiring monitor) → RUNNABLE`. ## Mental model - **BLOCKED** = "I want the lock; someone else has it." (lock *acquisition* contention) - **WAITING** = "I gave up the lock/CPU on purpose and I'll sit here until you signal me." (a *cooperative* wait) ## Diagnostic value In a thread dump (`jstack`): a pile of threads `BLOCKED` on the same lock points to a hot, contended `synchronized` section (a scalability bottleneck or, with cycles, a deadlock). A pile of `WAITING` threads is usually benign — e.g. an idle thread pool waiting on a work queue.
- Does a thread in WAITING hold the object's monitor?No. If it entered WAITING via Object.wait(), that call released the monitor. It must re-acquire the monitor when it wakes up, possibly passing through BLOCKED.
- Can you have a deadlock made of WAITING threads, or only BLOCKED?Classic monitor deadlocks show as BLOCKED cycles. But you can also deadlock with WAITING threads (e.g. mutual join, or wait() that's never notified) — though a lost-notify is a livelock/stuck-wait rather than the JVM's deadlock-detector classic.
saying these in an interview costs you the question
- Saying wait() puts the thread in BLOCKED — it's WAITING.
- Claiming a notified thread goes straight to RUNNABLE without re-acquiring the monitor.
- Thinking BLOCKED can be caused by join() or wait() — those cause WAITING/TIMED_WAITING.
- Believing a BLOCKED thread holds a lock — it's trying to acquire one.