When multiple threads compete for exclusive resources, two concurrency hazards arise: Deadlocks (where threads freeze waiting for each other's locks) and the need for Inter-Thread Communication (coordinating work via producer-consumer signaling).
Suppose Thread 1 locks Resource A and waits for Resource B. Meanwhile, Thread 2 locks Resource B and waits for Resource A. Both threads remain frozen forever:
// DEADLOCK HAZARD DEMONSTRATION:
Object lockA = new Object();
Object lockB = new Object();
// Thread 1: Locks A, then attempts B
Thread t1 = new Thread(() -> {
synchronized (lockA) {
try { Thread.sleep(50); } catch (Exception e) {}
synchronized (lockB) { System.out.println("T1 acquired both!"); }
}
});
// Thread 2: Locks B, then attempts A (Deadlock Trap!)
Thread t2 = new Thread(() -> {
synchronized (lockB) {
try { Thread.sleep(50); } catch (Exception e) {}
synchronized (lockA) { System.out.println("T2 acquired both!"); }
}
});
wait(), notify() & notifyAll()Methods defined on java.lang.Object allowing threads to synchronize work in producer-consumer pipelines:
class MessageBuffer {
private String message;
private boolean hasMessage = false;
// Consumer method
public synchronized String take() throws InterruptedException {
while (!hasMessage) {
wait(); // Releases lock and sleeps until producer notifies!
}
hasMessage = false;
notifyAll(); // Wakes up producer
return message;
}
// Producer method
public synchronized void put(String msg) throws InterruptedException {
while (hasMessage) {
wait(); // Waits for consumer to take previous message
}
this.message = msg;
this.hasMessage = true;
notifyAll(); // Signals consumer that data is ready!
}
}
Manually creating threads in enterprise servers is expensive. Let us explore Thread Pools with ExecutorService. Proceed to: Java ExecutorService & Pools →
Accelerate your Java mastery by studying these tightly connected core tutorials:
Monitor locks and mutexes.
Concurrency utilities.
Thread creation.
Virtual thread concurrency.