skip to content

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?

level: middleimportance: must knowfreq 61%

answer

  1. RUNNABLE = on/ready for CPU (but native I/O hides as RUNNABLE)
  2. BLOCKED = waiting for a synchronized monitor someone else holds
  3. WAITING = parked with no timeout (wait/join/park/await)
  4. TIMED_WAITING = parked with a deadline (sleep, timed waits)
  5. Only RUNNABLE burns CPU; take several dumps to spot 'stuck'

basics

~20 s

RUNNABLE = 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 s

Each 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

for a junior

Can name the four states and say roughly that RUNNABLE means running and the others mean waiting.

for a middle

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.

for a senior

Knows the native-I/O-as-RUNNABLE caveat, distinguishes normal idle pools from real stalls, and uses repeated dumps to confirm 'stuck'.

for a principal

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

context