In a jstack thread dump, what do the thread states RUNNABLE, BLOCKED, WAITING and TIMED_WAITING mean, and what does each tell you during triage?
answer
- RUNNABLE = on/ready for CPU (but native I/O hides as RUNNABLE)
- BLOCKED = waiting for a synchronized monitor someone else holds
- WAITING = parked with no timeout (wait/join/park/await)
- TIMED_WAITING = parked with a deadline (sleep, timed waits)
- Only RUNNABLE burns CPU; take several dumps to spot 'stuck'
basics
~20 sRUNNABLE = the thread is running or ready to run. BLOCKED = it's stuck waiting to enter a synchronized block another thread holds. WAITING = it's parked indefinitely until notified. TIMED_WAITING = same but with a timeout (e.g. sleep). They tell you whether a thread is busy or stuck and on what.
solid answer
~50 sEach thread in a dump reports a `java.lang.Thread.State`. RUNNABLE means it is executing or scheduled to execute on a CPU (note: it also covers threads blocked in native I/O, which the JVM can't see past). BLOCKED means it is waiting to acquire an intrinsic lock (a `synchronized` monitor) currently owned by another thread — the dump shows `waiting to lock` plus the owner. WAITING means it parked itself with no timeout — `Object.wait()`, `Thread.join()`, `LockSupport.park()`, or a `Condition.await()` — and will stay there until another thread wakes it. TIMED_WAITING is the same but with a deadline: `Thread.sleep(n)`, timed `wait`, `park(nanos)`, pool keep-alive. During triage: lots of BLOCKED on the same lock means contention; many WAITING on a connection pool means resource exhaustion; a RUNNABLE thread in your hot method across several dumps is your CPU burner. Crucially, WAITING/TIMED_WAITING threads consume no CPU — only RUNNABLE ones do.
go deeper
Can name the four states and say roughly that RUNNABLE means running and the others mean waiting.
Explains the cause of each state (synchronized monitor vs wait/park vs timed), knows only RUNNABLE uses CPU, and reads them from a real dump.
Knows the native-I/O-as-RUNNABLE caveat, distinguishes normal idle pools from real stalls, and uses repeated dumps to confirm 'stuck'.
Reasons about state distributions across a fleet, ties patterns (BLOCKED clusters, pool-exhaustion WAITING) to architectural fixes, and sets triage standards.
## Background: thread states and locks The JVM models each thread with a **state** from the `Thread.State` enum. A jstack dump prints this state on the line `java.lang.Thread.State: ...` for every thread, right above its stack trace. Understanding the four runtime states is the core skill of reading a dump. Two lock concepts appear in dumps: - A **monitor** (intrinsic lock) is the lock acquired by Java's `synchronized` keyword. Exactly one thread can own a given object's monitor at a time. - An **ownable synchronizer** is the lock used by `java.util.concurrent` (e.g. `ReentrantLock`), built on `LockSupport.park`. With `jstack -l` the dump also lists these. ## The four states ### RUNNABLE The thread is **running on a CPU or ready to run** (the OS may be time-slicing it). This is the only state that actually burns CPU. **Gotcha:** the JVM marks a thread RUNNABLE even when it is blocked deep inside a *native* call such as a socket `read()` — the JVM can't see past the native boundary, so a thread sitting idle on `socketRead0` still shows RUNNABLE. So RUNNABLE does not always mean 'busy'; check the actual stack frame. ### BLOCKED The thread is **waiting to enter a `synchronized` block/method** whose monitor another thread holds. The dump shows a line like `waiting to lock <0x...> (a com.example.Cache)` and, ideally, which thread is `locked` that same monitor. **Many threads BLOCKED on one monitor = lock contention**: they are serialized behind a hot critical section. This is the classic scalability bottleneck. ### WAITING The thread **voluntarily parked itself with no timeout** and will resume only when another thread signals it. It got here via `Object.wait()` (waiting on a `notify`), `Thread.join()` (waiting for another thread to die), `LockSupport.park()`, or `Condition.await()`. The dump shows `waiting on <0x...>` or `parking to wait for <0x...>`. A pool of worker threads idle for work legitimately sits in WAITING — that's normal. WAITING is suspicious only when a thread should be making progress but isn't. ### TIMED_WAITING Identical to WAITING but with a **deadline**, so it wakes on its own even if nobody signals it: `Thread.sleep(ms)`, timed `Object.wait(ms)`, `LockSupport.parkNanos`, `BlockingQueue.poll(timeout)`, thread-pool keep-alive. Seeing pool threads here between tasks is healthy. ### (The non-runtime states) NEW (not started) and TERMINATED (finished) also exist but rarely appear usefully in a dump. ## Reading states during triage | Symptom in the dump | Likely meaning | |---|---| | Many threads **BLOCKED** on the same monitor | Lock contention; one thread holds a hot lock | | Many threads **WAITING** to borrow from a pool | Connection/thread-pool exhaustion downstream | | One **RUNNABLE** thread in your method across several dumps | The CPU-burning hot loop | | Two threads each **BLOCKED** waiting on a lock the other holds | Deadlock (jstack labels it explicitly) | | Everything **WAITING/TIMED_WAITING**, app idle | Normal quiescent pool, or upstream starved it | **Key rule:** WAITING and TIMED_WAITING threads consume **no CPU**. If CPU is pegged, look only at RUNNABLE threads (and remember the native-I/O caveat). To distinguish a genuinely stuck thread from a momentary pause, take **several dumps a few seconds apart**: a thread on the *same* frame in every dump is stuck; one that moves is just busy doing work.
- A thread shows RUNNABLE but CPU is low — what's a common explanation?It's blocked inside a native call (e.g. socketRead0) that the JVM can't see past, so it reports RUNNABLE while actually waiting on I/O. Check the top stack frame.
- How do you tell a momentarily-busy thread from a permanently stuck one?Take several dumps a few seconds apart and compare; a thread on the same stack frame across all of them is stuck, one whose frame changes is making progress.
- What distinguishes BLOCKED from WAITING?BLOCKED is contending for a synchronized monitor another thread owns; WAITING is a thread that parked itself voluntarily (wait/join/park/await) until signaled.
saying these in an interview costs you the question
- Treating RUNNABLE as 'definitely busy' — a thread blocked in native socket read is still RUNNABLE
- Confusing BLOCKED (waiting for a synchronized monitor) with WAITING (parked via wait/park) — they have different causes
- Thinking WAITING threads are a problem; idle pool threads legitimately sit in WAITING/TIMED_WAITING
- Diagnosing high CPU by counting WAITING threads — only RUNNABLE threads consume CPU