Deadlocks
TL;DR
- A Deadlock is a catastrophic state where two or more threads are blocked forever, waiting for each other to release locks.
- Example: Thread A has Lock 1 and is waiting for Lock 2. Thread B has Lock 2 and is waiting for Lock 1. Neither can proceed.
- It completely freezes the affected threads in the application.
Concept
For a deadlock to occur, four conditions (Coffman Conditions) must simultaneously hold true:
- Mutual Exclusion: Resources cannot be shared; only one thread can hold a lock at a time.
- Hold and Wait: A thread holding a lock can wait to acquire additional locks.
- No Preemption: A lock cannot be forcefully taken away from a thread; it must be released voluntarily.
- Circular Wait: There exists a circular chain of threads, each waiting for a lock held by the next thread in the chain.
If you break any one of these four rules, a deadlock is mathematically impossible.
Examples
public class DeadlockExample {
public static void main(String[] args) {
final Object Lock1 = new Object();
final Object Lock2 = new Object();
Thread t1 = new Thread(() -> {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");
try { Thread.sleep(10); } catch (Exception e) {}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (Lock2) { // BLOCKS FOREVER HERE
System.out.println("Thread 1: Acquired lock 1 & 2");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 2...");
try { Thread.sleep(10); } catch (Exception e) {}
System.out.println("Thread 2: Waiting for lock 1...");
synchronized (Lock1) { // BLOCKS FOREVER HERE
System.out.println("Thread 2: Acquired lock 1 & 2");
}
}
});
t1.start();
t2.start();
}
}
Interview Questions
Q: How do you prevent Deadlocks in Java code?
A: The easiest and most common way to prevent deadlocks is to break the Circular Wait condition. You do this by establishing a strict, globally agreed-upon order in which locks must be acquired. In the example above, if we enforce a rule that says “Always acquire Lock1 before Lock2”, Thread 2 would be rewritten to request Lock1 first. It would block waiting for Thread 1, allowing Thread 1 to safely acquire Lock2 and finish its work.
Q: How do you diagnose a deadlock in a running application?
A: If a Java server freezes in production, you can take a Thread Dump using command-line tools like jstack <pid> or jcmd <pid> Thread.print. A thread dump takes a snapshot of the JVM. It will explicitly identify the blocked threads and state: Found one Java-level deadlock: followed by the exact class and line numbers where the threads are permanently stuck.