How to Diagnose and Prevent Java Deadlocks (Alibaba & Other Big‑Company Interviews)

This article explains what a deadlock is, the four necessary conditions that cause it, demonstrates classic synchronized‑based and Lock‑based deadlock examples in Java, shows how to detect deadlocks with tools such as Arthas, jstack, jvisualvm and JMC, and provides practical strategies—including lock ordering, timeout locks, and two‑phase locking—to break each condition and avoid deadlocks in production code.

Tech Freedom Circle
Tech Freedom Circle
Tech Freedom Circle
How to Diagnose and Prevent Java Deadlocks (Alibaba & Other Big‑Company Interviews)

What is a deadlock?

A deadlock occurs when two or more threads each hold a resource and wait indefinitely for the other’s resource, so none can proceed.

Four necessary conditions

Mutual exclusion : a resource can be held by only one thread at a time.

Hold and wait : a thread holding one resource requests another that is already held.

No preemption : a resource can be released only voluntarily by the owning thread.

Circular wait : a closed chain of threads each waiting for the next thread’s resource.

Deadlock example – synchronized locks

Two threads acquire two intrinsic locks in opposite order, each pausing for one second before trying the second lock, leading to a deadlock.

public class DeadLockExample {
    public static void main(String[] args) {
        Object lockA = new Object();
        Object lockB = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (lockA) {
                System.out.println("Thread 1: got lock A");
                try { Thread.sleep(1000); } catch (InterruptedException e) {}
                System.out.println("Thread 1: waiting for B");
                synchronized (lockB) {
                    System.out.println("Thread 1: got lock B");
                }
            }
        });
        Thread t2 = new Thread(() -> {
            synchronized (lockB) {
                System.out.println("Thread 2: got lock B");
                try { Thread.sleep(1000); } catch (InterruptedException e) {}
                System.out.println("Thread 2: waiting for A");
                synchronized (lockA) {
                    System.out.println("Thread 2: got lock A");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

Deadlock example – Lock (ReentrantLock)

The same scenario using explicit Lock objects.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class DeadLockByReentrantLockExample {
    public static void main(String[] args) {
        Lock lockA = new ReentrantLock();
        Lock lockB = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            lockA.lock();
            System.out.println("Thread 1: got lock A");
            try {
                Thread.sleep(1000);
                System.out.println("Thread 1: waiting for B");
                lockB.lock();
                try {
                    System.out.println("Thread 1: got lock B");
                } finally {
                    lockB.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lockA.unlock();
            }
        });
        Thread t2 = new Thread(() -> {
            lockB.lock();
            System.out.println("Thread 2: got lock B");
            try {
                Thread.sleep(1000);
                System.out.println("Thread 2: waiting for A");
                lockA.lock();
                try {
                    System.out.println("Thread 2: got lock A");
                } finally {
                    lockA.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lockB.unlock();
            }
        });
        t1.start();
        t2.start();
    }
}

Detecting deadlocks

Arthas : thread -b or thread --block lists blocked threads and their owners.

jstack : use jps to obtain the PID, then jstack -l PID to dump thread stacks with lock information.

jvisualvm : GUI tool with a “Detect Deadlock” button in the Threads tab.

Java Mission Control (JMC) : JMX console where deadlock detection can be enabled.

How to prevent deadlocks

Breaking any one of the four conditions eliminates the possibility of deadlock.

1. Break mutual exclusion

Use lock‑free data structures such as ConcurrentHashMap instead of synchronized Map.

Employ atomic classes ( AtomicInteger, AtomicReference) for simple shared state.

Give each thread its own ThreadLocal variables.

Apply read‑write locks ( ReentrantReadWriteLock) when reads dominate writes.

2. Break hold‑and‑wait

Two common approaches:

Atomic resource allocation : request all needed resources in a single atomic step.

import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;

public class AtomicResourceAllocator {
    private final Map<Object, Boolean> status = new ConcurrentHashMap<>();
    public boolean acquireAll(Object... resources) {
        for (Object r : resources) {
            if (status.putIfAbsent(r, true) != null) {
                // rollback already acquired resources
                for (Object a : resources) { if (a == r) break; status.remove(a); }
                return false;
            }
        }
        return true;
    }
    public void releaseAll(Object... resources) {
        for (Object r : resources) status.remove(r);
    }
}

Two‑phase locking (2PL) : separate an expansion phase (acquire only) from a shrinking phase (release only) to avoid acquiring new locks while holding others.

void transfer(Account from, Account to, int amount) {
    // expansion phase – acquire all needed locks
    synchronized (from) {
        synchronized (to) {
            // business logic
            if (from.balance >= amount) {
                from.balance -= amount;
                to.balance += amount;
            }
        }
    }
    // shrinking phase happens automatically as synchronized blocks exit
}

3. Break no‑preemption

Use timed lock acquisition: lock.tryLock(1, TimeUnit.SECONDS) so a thread gives up after a timeout.

Use interruptible locks: lock.lockInterruptibly() so a waiting thread can be interrupted.

Lock lock = new ReentrantLock();
if (lock.tryLock(1, TimeUnit.SECONDS)) {
    try {
        // critical section
    } finally {
        lock.unlock();
    }
} else {
    // timeout handling
}

// interruptible lock example
lock.lockInterruptibly();

4. Break circular wait

Global lock ordering : always acquire locks in a deterministic order, e.g., lock the object with the smaller ID first.

public void transfer(Account a, Account b, int amount) {
    Account first = a.getId() < b.getId() ? a : b;
    Account second = a.getId() < b.getId() ? b : a;
    synchronized (first) {
        synchronized (second) {
            // transfer logic
        }
    }
}

Hierarchical locking : lock higher‑level resources before lower‑level ones (e.g., database → table → row).

enum LockLevel { DATABASE(1), TABLE(2), ROW(3); }

void updateRecord(Database db, Table tbl, Row row) {
    synchronized (db) {
        synchronized (tbl) {
            synchronized (row) {
                // update
            }
        }
    }
}

Resource ID sorting : sort resources by identity hash code and lock them sequentially.

import java.util.Arrays;
import java.util.Comparator;

class CompositeResource {
    private final Object[] resources;
    CompositeResource(Object... rs) {
        this.resources = Arrays.stream(rs)
            .sorted(Comparator.comparingInt(System::identityHashCode))
            .toArray();
    }
    void lockAll() { for (Object r : resources) synchronized (r) {} }
    void unlockAll() { for (int i = resources.length - 1; i >= 0; i--) synchronized (resources[i]) {} }
}

Periodic deadlock detection and recovery

A background thread can poll the JVM every few seconds using ThreadMXBean.findDeadlockedThreads(), print detailed information, and optionally interrupt the deadlocked threads.

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

public class DeadlockDetector extends Thread {
    private static final long INTERVAL = 5000; // 5 seconds
    public void run() {
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        while (!isInterrupted()) {
            long[] deadlocked = bean.findDeadlockedThreads();
            if (deadlocked != null) {
                for (ThreadInfo ti : bean.getThreadInfo(deadlocked)) {
                    System.err.println("=== Deadlock detected ===");
                    System.err.println("Thread: " + ti.getThreadName());
                    System.err.println("Waiting for lock: " + ti.getLockName());
                    System.err.println("Lock owned by: " + ti.getLockOwnerName());
                    for (StackTraceElement e : ti.getStackTrace()) {
                        System.err.println("\t" + e);
                    }
                }
                // Aggressive recovery example: interrupt all deadlocked threads
                for (ThreadInfo ti : bean.getThreadInfo(deadlocked)) {
                    Thread.getAllStackTraces().keySet().stream()
                        .filter(t -> t.getId() == ti.getThreadId())
                        .findFirst()
                        .ifPresent(Thread::interrupt);
                }
            }
            try { Thread.sleep(INTERVAL); } catch (InterruptedException e) { break; }
        }
    }
}

In production you would replace the aggressive interrupt with a more nuanced recovery strategy (retry, resource cleanup, or fail‑over).

JavaconcurrencyDeadlockLockArthasThread Synchronizationjstack
Tech Freedom Circle
Written by

Tech Freedom Circle

Crazy Maker Circle (Tech Freedom Architecture Circle): a community of tech enthusiasts, experts, and high‑performance fans. Many top‑level masters, architects, and hobbyists have achieved tech freedom; another wave of go‑getters are hustling hard toward tech freedom.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.