Inter-Thread Communication in Java
Inter-thread communication in Java lets one thread wait until another thread tells it that something has happened, such as "the data is ready". It uses three methods of the Object class: wait(), notify() and notifyAll(). A thread calls wait() to pause and release the lock; another thread changes the shared state and calls notify() or notifyAll() to wake the waiters. All three must be called inside a synchronized block on the same object. In this lesson, you will learn the rules and the correct pattern.
Methods
| Method | Description |
|---|---|
wait() | It is used to release the object's lock and wait until notified (or interrupted). |
wait(long ms) | It waits at most the given time. |
notify() | It wakes one thread waiting on this object. |
notifyAll() | It wakes all threads waiting on this object. |
All three are final methods of Object, and all must be called while holding that object's lock; otherwise they throw IllegalMonitorStateException.
The Correct Pattern
// waiting side
synchronized (lock) {
while (!condition) {
lock.wait();
}
// condition is true: do the work
}
// signaling side
synchronized (lock) {
condition = true;
lock.notifyAll();
}Always wait in a while loop that rechecks the condition: a thread can wake up without the condition being true (a spurious wakeup, or another thread got there first).
Example: Waiting Until the Report Is Ready
Let's see an example where one thread waits until another has prepared a report.
public class ReadySignal {
private final Object lock = new Object();
private String report;
void waitForReport() throws InterruptedException {
synchronized (lock) {
while (report == null) {
System.out.println("Advitha waits for the report");
lock.wait();
}
System.out.println("Advitha reads: " + report);
}
}
void publish(String text) {
synchronized (lock) {
report = text;
System.out.println("Navika publishes the report");
lock.notifyAll();
}
}
public static void main(String[] args) throws InterruptedException {
ReadySignal r = new ReadySignal();
Thread reader = new Thread(() -> {
try {
r.waitForReport();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
reader.start();
Thread.sleep(200);
r.publish("Marks: 91, 78, 85");
reader.join();
}
}Advitha waits for the report
Navika publishes the report
Advitha reads: Marks: 91, 78, 85Explanation: The reader finds report is null and calls wait(), which releases the lock so the publisher can enter. The publisher sets the report and calls notifyAll(). The reader wakes, takes the lock back, rechecks the while condition, finds the report, and reads it.
notify() vs notifyAll()
notify() | notifyAll() | |
|---|---|---|
| Wakes | One waiting thread (which one is not specified) | All waiting threads |
| Risk | The woken thread may not be the one that can proceed, and the signal is lost | Some waste: woken threads may go back to waiting |
| Use when | All waiters wait for the same condition and any one can proceed | Waiters wait for different conditions (the safe default) |
Common Mistakes
Calling wait() outside synchronized. It throws IllegalMonitorStateException.
Using if instead of while around wait(). A spurious wakeup lets the thread continue with the condition still false.
Calling notify() before the waiter starts waiting, with no state flag. The signal is lost; always change a condition variable before notifying.
Interview Corner
Why must wait() and notify() be called inside a synchronized block?
wait() releases the object's monitor and notify() signals threads waiting on that monitor, so the calling thread must own the monitor; otherwise IllegalMonitorStateException is thrown. Holding the lock also makes checking the condition, waiting and changing the condition atomic, which prevents a lost-wakeup race where a notification is sent between a waiter's check and its call to wait().
Why should wait() be called in a loop?
Because a waiting thread can wake up even though the condition it's waiting for isn't true: spurious wakeups are allowed, notifyAll wakes threads waiting for different conditions, and another thread may change the state before the woken thread re-acquires the lock. Rechecking the condition in a while loop after every wakeup guarantees the thread continues only when it really can.
Practice
- Easy: Write a
Mailboxwithput(String)andtake()that hold at most one message, usingwaitandnotifyAll. - Harder: Make three threads print "1", "2", "3" in that order, repeated five times, coordinating with a shared turn counter and
wait/notifyAll.
Key Takeaways
Was this tutorial helpful?