1. The Hook (The "Byte-Sized" Intro)
- In a Nutshell: Deadlock occurs when threads wait for each other's locks indefinitely (circular wait).
- Four conditions: mutual exclusion, hold and wait, no preemption, circular wait—ALL must be true.
- Prevention: lock ordering, timeout, tryLock(). volatile ensures memory visibility—changes made by one thread visible to others. No atomicity! Use for flags, not counters. Prevents compiler optimizations that cache values in CPU registers.
Deadlock = two cars at narrow bridge, each waiting for other to reverse. volatile = megaphone announcement—everyone hears update immediately (no private notes)!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy
- Deadlock: You need my pen, I need your paper. Both refuse to release. Stuck forever!
- volatile: Shared whiteboard (everyone sees updates) vs personal notebook (private cache)
Deadlock Diagram
graph LR
T1[Thread 1] -->|holds| L1[Lock A]
T1 -->|waits for| L2[Lock B]
T2[Thread 2] -->|holds| L2
T2 -->|waits for| L1
style T1 fill:#C2185B
style T2 fill:#C2185B
3. Technical Mastery (The "Deep Dive")
Deadlock Example
java
class Resource {
public synchronized void method1(Resource other) {
System.out.println("Lock acquired on " + this);
try { Thread.sleep(10); } catch (Exception e) {}
other.method2(); // ❌ Waits for other's lock!
}
public synchronized void method2() {
System.out.println("Lock acquired on " + this);
}
}
// Thread 1: r1.method1(r2) // Holds r1, waits for r2
// Thread 2: r2.method1(r1) // Holds r2, waits for r1
// DEADLOCK!volatile Example
java
class StoppableTask implements Runnable {
private volatile boolean stopped = false; // ✅ volatile for visibility
@Override
public void run() {
while (!stopped) {
// Work
}
}
public void stop() {
stopped = true; // Other thread sees this immediately
}
}4. Interactive & Applied Code
java
public class DeadlockDemo {
static class Resource {
private String name;
public Resource(String name) { this.name = name; }
public synchronized void acquire(Resource other) {
System.out.println(Thread.currentThread().getName() +
" acquired lock on " + this.name);
try { Thread.sleep(100); } catch (Exception e) {}
System.out.println(Thread.currentThread().getName() +
" trying to acquire " + other.name);
synchronized(other) {
System.out.println("Acquired both locks!");
}
}
@Override
public String toString() { return name; }
}
public static void main(String[] args) {
Resource r1 = new Resource("Resource-1");
Resource r2 = new Resource("Resource-2");
// ❌ DEADLOCK: Different lock order
Thread t1 = new Thread(() -> r1.acquire(r2), "Thread-1");
Thread t2 = new Thread(() -> r2.acquire(r1), "Thread-2");
t1.start();
t2.start();
// Threads will deadlock—program hangs!
// ✅ DEADLOCK PREVENTION: Same lock order
demonstratePreventionWithOrdering();
demonstrateVolatile();
}
static void demonstratePreventionWithOrdering() {
// Always acquire locks in same order (by ID)
Resource r1 = new Resource("R1");
Resource r2 = new Resource("R2");
Runnable task = () -> {
// Always lock in same order
synchronized(r1) {
synchronized(r2) {
System.out.println("Acquired both safely");
}
}
};
new Thread(task).start();
new Thread(task).start();
}
static void demonstrateVolatile() {
VolatileExample example = new VolatileExample();
Thread writer = new Thread(() -> {
try { Thread.sleep(1000); } catch (Exception e) {}
example.stop();
System.out.println("Stop flag set");
});
Thread reader = new Thread(() -> {
example.run();
System.out.println("Reader stopped");
});
reader.start();
writer.start();
}
}
class VolatileExample {
private volatile boolean running = true;
public void run() {
while (running) {
// Without volatile, this might loop forever!
// Compiler might cache 'running' in register
}
}
public void stop() {
running = false;
}
}5. The Comparison & Decision Layer
| Feature | synchronized | volatile |
|---|---|---|
| Visibility | ✅ Yes | ✅ Yes |
| Atomicity | ✅ Yes | ❌ No |
| Performance | Slower (locking) | Faster (no lock) |
| Use for | Critical sections | Flags, status variables |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question:
"Why can't you use volatile for a counter?"
Answer: volatile doesn't guarantee atomicity! count++ is 3 operations:
java
private volatile int count = 0;
public void increment() {
count++; // ❌ NOT ATOMIC!
// 1. Read count
// 2. Increment
// 3. Write count
// Another thread can interfere between steps!
}
// ✅ Solutions:
// 1. synchronized
public synchronized void increment() { count++; }
// 2. AtomicInteger
private AtomicInteger count = new AtomicInteger(0);
public void increment() { count.incrementAndGet(); }Pro-Tip: Deadlock prevention strategies:
- Lock ordering: Always acquire locks in same global order
- Timeout: Use
tryLock(timeout)instead oflock() - Lock hierarchy: Assign numeric levels, always lock lower→higher
- Avoid nested locks: Minimize complexity
java
// ✅ GOOD: Lock ordering by ID
void transfer(Account from, Account to, int amount) {
Account first = from.id < to.id ? from : to;
Account second = from.id < to.id ? to : from;
synchronized(first) {
synchronized(second) {
from.debit(amount);
to.credit(amount);
}
}
}