Inter-Thread Communication
Inter-thread communication in Java allows multiple threads to coordinate and cooperate when working on shared data. It is primarily achieved using the wait(), notify(), and notifyAll() methods of the Object class, in conjunction with synchronization. This is a very high-frequency interview topic, especially with producer–consumer scenarios.
Why Inter-Thread Communication Is Needed
Without coordination, threads may:
- Run in the wrong order
- Waste CPU cycles (busy waiting)
- Read inconsistent data
- Cause race conditions
Goal: Enable threads to pause, resume, and signal each other safely.
Core Methods for Inter-Thread Communication
All belong to java.lang.Object:
| Method | Purpose |
|---|---|
| wait() | Releases lock and waits |
| notify() | Wakes up one waiting thread |
| notifyAll() | Wakes up all waiting threads |
Important Rule (Interview Favorite)
wait(), notify(), and notifyAll() must be called inside a synchronized context.
Otherwise: IllegalMonitorStateException
How wait() Works
- Causes the current thread to release the monitor lock
- Thread enters WAITING or TIMED_WAITING state
- Thread resumes only after:
- notify() / notifyAll()
- Re-acquiring the lock
synchronized (lock) {
lock.wait();
}
How notify() Works
- Wakes one arbitrary waiting thread
- Does not release the lock immediately
- Lock is released only after synchronized block exits
synchronized (lock) {
lock.notify();
}
How notifyAll() Works
- Wakes all waiting threads
- Threads compete to re-acquire the lock
- Safer when multiple conditions exist
synchronized (lock) {
lock.notifyAll();
}
Thread State Transitions
| Method | State Change |
|---|---|
| wait() | RUNNABLE → WAITING |
| notify() | WAITING → BLOCKED |
| Lock acquired | BLOCKED → RUNNABLE |
Classic Example: Producer–Consumer Problem
Shared Resource
class SharedBuffer {
private int data;
private boolean available = false;
synchronized void produce(int value) throws InterruptedException {
while (available) {
wait();
}
data = value;
available = true;
System.out.println("Produced: " + value);
notify();
}
synchronized int consume() throws InterruptedException {
while (!available) {
wait();
}
available = false;
System.out.println("Consumed: " + data);
notify();
return data;
}
}
✔ Uses while, not if
✔ Avoids spurious wakeups
Why while Is Used Instead of if (Very Important)
- Threads may wake up without condition being true
- Multiple threads may be notified
- while rechecks condition
while (!condition) {
wait();
}
wait() vs sleep() (Common Interview Trap)
| Aspect | wait() | sleep() |
|---|---|---|
| Releases lock | ✔ Yes | ❌ No |
| Called on | Object | Thread |
| Used for | Coordination | Pause |
| Requires synchronized | ✔ Yes | ❌ No |
notify() vs notifyAll()
| Aspect | notify() | notifyAll() |
|---|---|---|
| Threads awakened | One | All |
| Performance | Better | Slower |
| Safety | Risky | Safer |
| Use case | Single-condition | Multiple conditions |
Common Mistakes
- Calling wait() outside synchronized block
- Using if instead of while
- Using notify() when multiple threads wait
- Synchronizing on wrong object
- Forgetting condition flags
Best Practices (Production-Grade)
- Always use wait() in a while loop
- Prefer notifyAll() for correctness
- Keep synchronized blocks small
- Clearly define condition variables
- Consider java.util.concurrent alternatives
Modern Alternatives (Preview)
Instead of low-level wait/notify:
- BlockingQueue
- CountDownLatch
- CyclicBarrier
- Semaphore
✔ Safer
✔ Easier to maintain
✔ Preferred in enterprise code
Interview-Ready Answers
Short Answer
Inter-thread communication allows threads to coordinate using wait, notify, and notifyAll.
Detailed Answer
In Java, inter-thread communication is achieved using wait(), notify(), and notifyAll() methods of the Object class. These methods allow threads to release locks, wait for conditions, and signal other threads, enabling safe coordination and avoiding busy waiting in multithreaded programs.
Key Takeaway
wait() releases the lock.
notify() signals waiting threads.
notifyAll() ensures correctness.
Inter-thread communication is powerful but error-prone—use it carefully or prefer high-level concurrency utilities.