Usamos cookies essenciais para login e armazenamento local para suas preferências. Para métricas de uso, usamos o Google Analytics 4 — você pode recusar sem perder nada do produto. Saber mais

ThreadMineThreadMine
Back

Guia técnico

BLOCKED vs WAITING vs TIMED_WAITING: what Java thread states really mean

The state on a thread dump line tells you less than you think. A field guide to the six Java thread states, the dump lines that produce them, and how to tell a healthy idle thread from a stuck one.

Try it free

You open a thread dump, scan a hundred lines, and each thread carries a state: RUNNABLE, BLOCKED, WAITING, TIMED_WAITING. The instinct is to treat the state as the diagnosis — "lots of WAITING, the app is stuck." That instinct is wrong often enough to cost you an incident. The state is a hint about how a thread is parked, not about whether anything is actually broken. This guide walks each state, shows the dump line that produces it, and — the part that matters — how to tell a perfectly healthy idle thread from one that is genuinely hung.

The six Java thread states

Every live thread reports one value from the java.lang.Thread.State enum. There are exactly six, and only three of them show up in an interesting way in a real dump:

  • NEW — created with new Thread() but start() was never called. You almost never see this in a dump; the thread is not scheduled yet.
  • RUNNABLE — executing in the JVM, or ready to run and waiting only for a CPU core (or for the OS to return from a native/IO call). See the trap in the next section.
  • BLOCKED — waiting to acquire a monitor lock to enter or re-enter a synchronized block or method. Always about a monitor; always a sign of contention.
  • WAITING — waiting indefinitely for another thread to act: Object.wait() with no timeout, Thread.join() with no timeout, or LockSupport.park().
  • TIMED_WAITING — the same as WAITING, but with a deadline: Thread.sleep(n), Object.wait(timeout), Thread.join(timeout), LockSupport.parkNanos() / parkUntil().
  • TERMINATEDrun() has returned. The thread is dead; it is gone from most dumps.

RUNNABLE does not mean running

The most common misread is not about the three -WAITING states — it is about RUNNABLE. A thread blocked inside a native socket read reports RUNNABLE, because as far as the JVM is concerned it is running native code; the OS, not the JVM, is doing the waiting. So a dump full of RUNNABLE threads sitting on socketRead0 is not a busy CPU — it is a fleet of threads blocked on a slow downstream, and your CPU may be idle. Read the top stack frame before you trust the word RUNNABLE.

BLOCKED — monitor contention

BLOCKED has exactly one meaning: the thread wants to enter a synchronized block or method and another thread currently owns that object's monitor. The dump makes it explicit with waiting to lock <0x…> on a monitor that some other thread reports as locked <0x…>.

One BLOCKED thread is normal — locks exist to be contended. The signal to chase is many threads BLOCKED on the same monitor address. That is a contention hotspot: a single synchronized section that has become a bottleneck, or a lock held while doing slow work (IO inside the critical section). If those threads never make progress and the holder is itself waiting on one of them, you may be looking at a deadlock rather than plain contention — the JVM will say so explicitly if it is one.

WAITING — idle, or silently stuck

WAITING is the state most people over-read. It means the thread parked itself with no timeout and will not wake up until another thread explicitly signals it. Crucially, that describes both of these:

  • A healthy idle worker. A thread-pool worker calling LinkedBlockingQueue.take() parks in WAITING until a task arrives. A dump taken on an idle server shows dozens of these. Nothing is wrong.
  • A genuinely stuck thread. A thread in Object.wait() for a notification that was already sent (a lost-notify bug), or one leg of a ReentrantLock-based deadlock, sits in the exact same WAITING state — forever.

The state is identical; only the stack tells them apart. WAITING on a queue's take() is idle-healthy. WAITING to acquire an application lock, or on a condition that nothing signals, is a problem. You cannot decide from the state word — you have to read the frame below it.

TIMED_WAITING — usually benign

TIMED_WAITING is WAITING with an alarm clock: the thread will wake on its own when the timeout expires, even if nobody signals it. This is the least alarming state — a scheduler in Thread.sleep(), a poller doing queue.poll(500, MILLISECONDS), a connection pool doing parkNanos while waiting for a lease. Because it self-recovers, TIMED_WAITING almost never deadlocks.

The one thing worth checking: a thread that is always TIMED_WAITING with a very long timeout on every dump you take is effectively idle for that window — fine if it is a poller, suspicious if it is supposed to be serving a request. Sample multiple dumps a few seconds apart; a benign timed wait moves, a stuck one does not.

Idle vs stuck: the state alone lies

Put the three together and the rule is simple: the state tells you the mechanism, the stack and the lock tell you the meaning.

  • Read the top frame. queue.take() / Unsafe.park under a pool worker = idle. An application method under a monitor = work in progress or contention.
  • Read the lock lines. waiting to lock <0x…> plus a holder that never releases is the stuck signature. parking to wait for a pool's own condition is the idle signature.
  • Compare dumps over time. Take two or three a few seconds apart. Threads that change state or stack are alive; threads frozen on the exact same frame and lock across all dumps are the ones actually hung.

One dump line per state

BLOCKED (on object monitor)

"http-nio-8080-exec-7" #41 daemon prio=5 tid=0x... nid=0x... waiting for monitor entry [0x...]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.example.Cache.get(Cache.java:42)
	- waiting to lock <0x000000076ab00000> (a com.example.Cache)
	at com.example.Api.handle(Api.java:88)

The waiting to lock <0x…ab00000> is the tell. Some other thread in the same dump holds that exact monitor. Find it — that holder is your bottleneck.

WAITING (parking) — a healthy idle pool worker

"pool-2-thread-1" #22 prio=5 tid=0x... nid=0x... waiting on condition [0x...]
   java.lang.Thread.State: WAITING (parking)
	at jdk.internal.misc.Unsafe.park(Native Method)
	- parking to wait for <0x000000076ab10000> (a j.u.c.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:435)

WAITING, but the bottom frame is LinkedBlockingQueue.take() — this worker is idle and waiting for work. Twenty of these on a quiet server is exactly what you want to see, not a problem.

TIMED_WAITING (sleeping)

"scheduler-1" #18 prio=5 tid=0x... nid=0x... waiting on condition [0x...]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at com.example.Scheduler.loop(Scheduler.java:31)

Self-recovering: this thread wakes on its own when the sleep expires. Benign unless the sleep interval is wrong for what the thread is supposed to be doing.

How ThreadMine classifies this automatically

ThreadMine does not just print the state next to each thread — it does the reading you would do by hand. It groups threads by state, then for every BLOCKED thread it resolves the monitor address to the thread that holds it, so a contention hotspot (many threads waiting on one lock) surfaces as a single ranked finding instead of a wall of lines.

For WAITING and TIMED_WAITING threads it reads the stack, not just the state: a pool worker parked on queue.take() is labelled idle and kept out of your way, while a thread waiting on an application lock, a never-signalled condition, or a lock cycle is flagged as suspect. That is the difference between "80 threads WAITING" (which panics you for nothing) and "3 threads stuck on the same lock, 77 idle workers" (which points you at the actual bug). Bring a dump and ThreadMine tells you in seconds which of your parked threads are healthy and which are hung.

Conclusion

Thread states are a vocabulary, not a diagnosis. BLOCKED is the one to take seriously by default — it always means monitor contention. WAITING and TIMED_WAITING are ambiguous by design: most of the time they are idle threads doing exactly what they should, and occasionally they are the smoking gun. The word never decides it — the stack and the lock do. Read those, compare a couple of dumps over time, and let the tool group the noise so the one stuck thread is not hiding among eighty healthy ones.

Want to see all this applied to your own dump?

Upload a dump and get a health score, detected problems and fixes in seconds. No signup for the first dump.