Interview Tip: Show a live deadlock code that actually hangs — it's dramatic and memorable. Then show the fix.
Deadlock happens when two or more threads wait for each other's locks forever, and none can proceed.
Classic scenario:
- Thread 1 holds Lock A, waits for Lock B
- Thread 2 holds Lock B, waits for Lock A
- → Both wait forever → Deadlock
public class DeadlockDemo {
static final Object LOCK_A = new Object();
static final Object LOCK_B = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (LOCK_A) {
System.out.println("Thread1 acquired LOCK_A");
try { Thread.sleep(100); } catch (Exception e) {}
synchronized (LOCK_B) { // waits for LOCK_B
System.out.println("Thread1 acquired LOCK_B");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (LOCK_B) {
System.out.println("Thread2 acquired LOCK_B");
try { Thread.sleep(100); } catch (Exception e) {}
synchronized (LOCK_A) { // waits for LOCK_A
System.out.println("Thread2 acquired LOCK_A");
}
}
});
thread1.start();
thread2.start();
// Program hangs here — deadlock!
}
}public class DeadlockFix {
static final Object LOCK_A = new Object();
static final Object LOCK_B = new Object();
public static void main(String[] args) {
// Both threads acquire LOCK_A first, then LOCK_B
Thread thread1 = new Thread(() -> {
synchronized (LOCK_A) {
synchronized (LOCK_B) {
System.out.println("Thread1 done");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (LOCK_A) { // same order as Thread1
synchronized (LOCK_B) {
System.out.println("Thread2 done");
}
}
});
thread1.start();
thread2.start();
// No deadlock — predictable lock ordering
}
}All 4 must be true simultaneously:
- Mutual Exclusion — resource held by one thread at a time
- Hold and Wait — thread holds one lock and waits for another
- No Preemption — lock can't be forcibly taken away
- Circular Wait — Thread1 waits for Thread2, Thread2 waits for Thread1
| Strategy | How |
|---|---|
| Lock ordering | Always acquire locks in same fixed order |
| Timeout | Use tryLock(timeout) from ReentrantLock |
| Avoid nested locks | Don't hold lock while acquiring another |
Use java.util.concurrent |
ReentrantLock, Semaphore have timeout support |
// tryLock with timeout — avoids deadlock
ReentrantLock lock = new ReentrantLock();
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try { /* do work */ }
finally { lock.unlock(); }
} else {
System.out.println("Could not acquire lock — skipping");
}- Deadlock = circular dependency between threads waiting on each other's locks
- Detect with thread dump →
jstack <pid>shows "BLOCKED" threads in cycle ReentrantLock.tryLock()→ best prevention in production codesynchronizedblocks can deadlock if lock order isn't consistent