60. Deadlock Detection and Avoidance
Deadlock occurs when two or more threads each hold a lock the other needs; avoid it with consistent lock ordering, tryLock with timeout, or lock-free designs; detect it with jstack or JFR.
Explanation
Classic deadlock: Thread A holds lock1, waiting for lock2. Thread B holds lock2, waiting for lock1. Neither can proceed. All four Coffman conditions must hold: mutual exclusion, hold-and-wait, no preemption, circular wait.
Prevention strategies: (1) Lock ordering: always acquire locks in a fixed global order (e.g., by object identity System.identityHashCode). If every thread acquires lock1 before lock2, circular wait is impossible. (2) tryLock with timeout: try to acquire, and if you can't in time, release all held locks and retry. (3) Reduce lock scope: use ConcurrentHashMap, AtomicInteger, etc., to avoid explicit locking.
Detection: jstack <pid> prints all thread stacks and reports Found one Java-level deadlock. JFR (Java Flight Recorder) can detect deadlocks and deadlock-prone contention in production.
Example
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockDemo {
static final ReentrantLock lockA = new ReentrantLock();
static final ReentrantLock lockB = new ReentrantLock();
// DEADLOCK: Thread 1 takes A then B, Thread 2 takes B then A
static void deadlockTask1() throws InterruptedException {
lockA.lock();
try {
Thread.sleep(10);
lockB.lock(); // waits for Thread 2 to release B
try { System.out.println("Task 1 has both locks"); }
finally { lockB.unlock(); }
} finally { lockA.unlock(); }
}
// FIX: use tryLock with timeout โ backs off if can't get both
static void safeTask(int id) throws InterruptedException {
while (true) {
if (lockA.tryLock()) {
try {
if (lockB.tryLock()) {
try {
System.out.println("Thread " + id + " has both locks");
return;
} finally { lockB.unlock(); }
}
} finally { lockA.unlock(); }
}
Thread.sleep(1); // back off before retrying
}
}
public static void main(String[] args) throws InterruptedException {
// Safe version: no deadlock
Thread t1 = new Thread(() -> { try { safeTask(1); } catch (InterruptedException e) {} });
Thread t2 = new Thread(() -> { try { safeTask(2); } catch (InterruptedException e) {} });
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println("Both tasks completed safely");
// To demonstrate deadlock detection:
// jstack <pid> | grep -A 20 "Found one Java-level deadlock"
}
}