A deadlock occurs when two or more threads wait indefinitely for locks or resources held by one another. The application usually freezes because no blocked thread can continue. section 8.0 section 8.3
Deadlocks commonly arise when synchronized code acquires multiple monitors in inconsistent orders. One thread can hold listA while waiting for listB, while another holds listB while waiting for listA. section 8.1
Investigate a frozen Java application with a thread dump:
- Reproduce or observe the freeze, then capture a snapshot of all JVM thread states with VisualVM, another profiler, or a JDK command-line tool. section 8.1.1 section 8.3
- Discard threads that are not blocked and inspect each blocked thread’s state, stack trace, held locks, and lock it is waiting to acquire. section 8.2.1
- Follow lock IDs from a waiting thread to the thread holding that lock. If the chain returns to an already inspected thread, the cycle identifies the deadlock. section 8.2.1
- Use instrumentation or lock profiling when a snapshot is insufficient and execution dynamics, contention, or lock duration must be investigated. section 7.4 section 8.3
Reversed monitor acquisition
A Consumer thread enters synchronized(listA) and then waits for listB. A Producer thread enters synchronized(listB) and then waits for listA. Both remain BLOCKED, producing a simple two-thread deadlock. section 8.1 section 8.2.1
Listing 8.1 Using nested synchronized blocks for the
"_Consumer" thread
public class Consumer extends Thread {
// Omitted code
@Override
public void run() {
while (true) {
synchronized (Main.listA) { #1
synchronized (Main.listB) { #2
work();
}
}
}
}
// Omitted code
}
Compare the Consumer and Producer synchronized blocks to see how reversing the outer and inner monitors creates the waiting cycle. section 8.1














