Concurrency

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.

Java 21 (core language)

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"
    }
}

Sources