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.
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).
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
