Write code that deadlocks and explain why. — Cracked Java
// Concurrency & Multithreading · Deadlock, Livelock & Starvation
SeniorCodingBig TechAmazonMeta

Write code that deadlocks and explain why.

The classic deadlock is two threads acquiring the same two locks in opposite order: thread 1 takes A then wants B, thread 2 takes B then wants A. Each holds what the other needs, so both block forever.

The code

public class TwoLockDeadlock {
    private static final Object LOCK_A = new Object();
    private static final Object LOCK_B = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (LOCK_A) {
                System.out.println("T1 holds A, wants B");
                sleep(50);                 // give T2 time to grab B
                synchronized (LOCK_B) {     // blocks: T2 holds B
                    System.out.println("T1 got both");
                }
            }
        }, "T1");

        Thread t2 = new Thread(() -> {
            synchronized (LOCK_B) {         // opposite order!
                System.out.println("T2 holds B, wants A");
                sleep(50);
                synchronized (LOCK_A) {     // blocks: T1 holds A
                    System.out.println("T2 got both");
                }
            }
        }, "T2");

        t1.start();
        t2.start();
    }

    static void sleep(long ms) {
        try { Thread.sleep(ms); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
    }
}

Why it deadlocks

The sleep(50) is not the cause — it just makes the race deterministic. The real cause is inconsistent lock ordering: T1 acquires A → B, T2 acquires B → A. Once T1 owns A and T2 owns B, T1's request for B and T2's request for A can never be satisfied. All four Coffman conditions hold: the monitors are mutually exclusive, each thread holds one while waiting for the other, the JVM never preempts a monitor, and the wait graph is a cycle.

This pattern hides everywhere in real systems: a bank transfer(from, to) where one thread moves A→B while another moves B→A; two services each locking a shared cache and a DB row in different orders; nested synchronized collections.

The fix is to impose a global order so every thread takes the lower-ranked lock first (e.g. by System.identityHashCode), making circular wait impossible.

Mark your status