Definition
- Thread dump – snapshot of every thread: name, state, call stack, locks held and awaited.
jstack– most common capture tool;-ladds lock details.kill -3– sends SIGQUIT; JVM prints the dump to its own stdout.jcmd– modern, preferred tool; supportsThread.print -lfor lock details.- States – RUNNABLE, BLOCKED, WAITING, TIMED_WAITING.
- Lock lines –
lockedmeans holds it;waiting to lockmeans blocked trying to acquire it. - Deadlock section – the JVM prints the cycle when it detects one.
A thread dump is like freezing time in National Bank of India and photographing every teller: what each is doing, which vault key they are holding, and which key they are waiting for. If teller A holds the gold-vault key but waits for the cash-vault key while teller B holds the cash-vault key but waits for the gold-vault key, the photograph shows you the standoff: a deadlock.
Understanding the Concepts
How do you capture a thread dump?
Three standard methods, each useful in different situations.
# Most common: jstack with extended lock info
jps # find the PID of the Java process
jstack -l <pid> > dump.txt # capture with lock details
# Alternative: SIGQUIT (prints to process stdout or log file)
kill -3 <pid> # does NOT kill the process
# Modern preferred: jcmd
jcmd <pid> Thread.print -l > dump.txt
Use jstack -l for the most detailed lock information. Use kill -3 when you cannot attach to the JVM (security restrictions). Use jcmd for the most modern and flexible approach.
How do you read thread states?
Each thread in the dump has a state that tells you what it is doing.
| State | Meaning | Common Cause |
|-------|---------|--------------|
| RUNNABLE | Executing or ready to execute | Actually working or in native IO |
| BLOCKED | Waiting to acquire a monitor lock | synchronized contention |
| WAITING | Parked indefinitely | wait(), LockSupport.park(), ReentrantLock |
| TIMED_WAITING | Parked with timeout | sleep(), parkNanos(), tryLock(timeout) |
How do you read lock ownership?
Look for two key lines: locked means the thread holds the lock; waiting to lock means the thread is blocked trying to acquire it. Match the hex identity hash code across threads to find who holds what.
"settlement-pool-thread-3" BLOCKED
at com.bank.service.TransferService.transfer(TransferService.java:42)
- waiting to lock <0x00000007c0045678> (a com.bank.model.Account)
"settlement-pool-thread-7" RUNNABLE
at com.bank.service.TransferService.transfer(TransferService.java:38)
- locked <0x00000007c0045678> (a com.bank.model.Account)
Thread 3 is BLOCKED waiting for the Account lock that thread 7 holds. Thread 7's stack trace shows it is executing a database query inside the synchronized block.
How do you map a hot OS thread to a Java thread?
In the thread dump, each thread has an nid field (native thread ID) in hexadecimal. Use top -H -p on Linux to find the OS thread consuming the most CPU, convert its PID to hex, and match it to the nid in the dump.
# Find the hot OS thread
top -H -p <pid>
# Say thread 12345 is using 99% CPU
printf '%x\n' 12345 # outputs 3039
# Search the dump for nid=0x3039
grep 'nid=0x3039' dump.txt
How do you read the deadlock section?
When the JVM detects a deadlock, it prints the exact cycle at the end of the dump.
Found one Java-level deadlock:
=============================
"transfer-A":
waiting to lock <0x...Account@priya>
which is held by "transfer-B"
"transfer-B":
waiting to lock <0x...Account@rajesh>
which is held by "transfer-A"
Fix: impose a global lock ordering. Always lock the account with the smaller ID first.
package com.bank.service;
import java.math.BigDecimal;
public class SafeTransferService {
public void transfer(Account from, Account to, BigDecimal amount) {
Account first = from.getId().compareTo(to.getId()) < 0 ? from : to;
Account second = first == from ? to : from;
synchronized (first) {
synchronized (second) {
from.debit(amount);
to.credit(amount);
}
}
}
}
Why take multiple dumps?
One dump is a snapshot. A thread that appears BLOCKED in one dump might be working normally a second later. Comparing two or three dumps taken 5-10 seconds apart shows which threads are moving forward and which are stuck at the same stack frame. Threads stuck at the same frame across multiple dumps are genuinely blocked.
What are common mistakes?
Using anonymous thread names (Thread-0, Thread-1) makes dumps unreadable. Always name your threads with a meaningful prefix via a custom ThreadFactory.
What are common interview follow-up questions?
- How do you capture a heap dump versus a thread dump? Thread dump:
jstackorjcmd Thread.print. Heap dump:jmap -dump:format=b,file=heap.hproforjcmd.GC.heap_dump heap.hprof - How does jcmd Thread.dump_to_file work in newer JVMs? It writes the dump directly to a file, useful in containerized environments where stdout is not easily captured.
- What is the difference between BLOCKED and WAITING? BLOCKED is waiting for a synchronized monitor. WAITING is parked by LockSupport, wait(), or join().
Visual Overview
Thread Dump Reading:
"settlement-pool-thread-3" BLOCKED
at TransferService.transfer(:42)
- waiting to lock <0x...Account@priya>
"settlement-pool-thread-7" RUNNABLE
at TransferService.transfer(:38)
- locked <0x...Account@priya>
- locked <0x...Account@rajesh>
Deadlock Detection:
Thread A: holds rajesh → waits priya
Thread B: holds priya → waits rajesh
┌──────────────────┐
│ DEADLOCK CYCLE │
│ A → priya → B │
│ B → rajesh → A │
└──────────────────┘
Fix: always lock smaller account ID first
Key Takeaway
A thread dump is the first tool I reach for when a service stalls. I capture it with jstack -l, jcmd Thread.print, or kill -3. I read the thread name, state, lock lines, and the deadlock section. Matching the hex lock identity across threads shows who holds what and who is waiting.
I always take two or three dumps seconds apart to distinguish stuck threads from normal work, and I name my threads so the dump is instantly readable.
Remember one sentence: capture a thread dump with jstack or jcmd, read the state and lock lines to find who is stuck and who holds the lock, and the deadlock section tells you the exact cycle to fix with a global lock ordering.
---