skip to content

What is the difference between the BLOCKED and WAITING thread states, and what transitions a thread into each?

level: middleimportance: must knowfreq 60%

answer

  1. BLOCKED = wants a monitor lock (involuntary)
  2. WAITING = called wait/join/park, no timeout (voluntary)
  3. wait() releases the monitor; BLOCKED is trying to get one
  4. After notify: WAITING -> BLOCKED (re-acquire) -> RUNNABLE
  5. Dump: many BLOCKED = contention/deadlock; many WAITING = idle pool

basics

~20 s

BLOCKED 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 s

BLOCKED 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 lines
java
Object 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

for a junior

Knows BLOCKED is about waiting for a lock and WAITING is about wait()/join(), even if the monitor re-acquisition detail is fuzzy.

for a middle

Cleanly separates involuntary lock contention (BLOCKED) from voluntary suspension (WAITING) and names the triggering calls for each.

for a senior

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.

for a principal

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.

context