Java Thread Waiting and Notification: wait, notify, and LockSupport
notify():
Wakes up a single thread waiting on this object's monitor. The thread returns from wait only after it re-acquires the lock. If no lock is obtained, the thread goes back to the WAITING state.
notifyAll():
Wakes up all threads waiting on this object's monitor.
wait():
Causes the current thread to enter the WAITING state until another thread notifies it or it is interrupted. Note that calling wait() releases the object’s lock.
wait(long timeout):
Waits for at most timeout milliseconds. Returns if not notified before the timeout.
Standard usage pattern
Waiting side
synchronized(monitor) {
while(condition not met) {
monitor.wait();
}
}
Notifying side
synchronized(monitor) {
// change the condition
monitor.notify();
}
Why synchronization is required
Assume party A is the waiter and party B is the notifier. If A checks the condition (finds it false) and then loses the CPU before calling wait, by that time B may have already changed the condition and called notify. When A resumes, it calls wait and may wait forever. Synchronization prevents this gap.
Example: Weighbridge System
Three steps are involved:
- Vehicle license plate recognition (cannot weigh because the driver hasn't left the vehicle yet).
- Capture vehicle photo (cannot capture because the driver hasn't left the vehicle yet).
- The driver gets out and swipes a card at the reader, wich wakes up the tasks in steps 1 and 2.
public class WeighbridgeDemo {
static boolean driverCardSwiped = false;
public static void main(String[] args) {
Object monitor = new Object();
// Weighing thread
new Thread(() -> {
System.out.println("[Weighbridge] Camera recognized vehicle, waiting for driver to swipe card...");
while (!driverCardSwiped) {
synchronized (monitor) {
try {
monitor.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
System.out.println("[Weighbridge] Card swiped, starting weighing...");
}).start();
// Photo capture thread
new Thread(() -> {
System.out.println("[Weighbridge] Capturing photo...");
while (!driverCardSwiped) {
synchronized (monitor) {
try {
monitor.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
System.out.println("[Weighbridge] Photo capture completed.");
}).start();
// Card‑swipe thread
new Thread(() -> {
try {
Thread.sleep(3000);
synchronized (monitor) {
driverCardSwiped = true;
monitor.notifyAll();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
}
Should we use notify or notifyAll?
Prefer notifyAll() whenever possible. Use notify() with caution because it only wakes one thread, and you cannot guarantee that the woken thread is the one you actually need to proceed.
When do wait and notify release the lock?
wait: releases the lock immediately upon invocation. When it is woken up it will re‑compete for the lock, and only after re‑acquiring it will continue execution after wait().
notify: the lock is released only when the enclosing synchronized block fully completes. Notify usually appears at the end of the block because you want to first finish all condition modifications before alerting other threads.
(2) Waking up a specific thread
The notify family of methods cannot wake a specific thread. To wake a designated thread you can use LockSupport:
public class LockSupportDemo {
public static void main(String[] args) {
Thread worker1 = new Thread(() -> {
LockSupport.park();
System.out.println("Worker-1 running");
});
Thread worker2 = new Thread(() -> {
LockSupport.park();
System.out.println("Worker-2 running");
});
worker1.start();
worker2.start();
LockSupport.unpark(worker1);
}
}
When park() is executed it first checks for a permit. With out a permit the thread blocks.
unpark(thread) issues a permit, waking the specified thread if it was blocked due to lack of a permit.
Note: park and unpark have no ordering requirement. If unpark is called first, a subsequetn park will return immediately.