notify() wakes one arbitrary thread from the monitor's wait set; notifyAll() wakes them all to compete for the lock. notify() is cheaper but only safe under strict conditions — most of the time you should default to notifyAll().
The risk with notify()
If multiple threads wait on the same monitor for different conditions, notify() may wake the wrong thread — one whose predicate is still false. That thread re-checks (it's in a while loop), goes back to wait(), and the thread that could have proceeded was never signaled. This is a lost wakeup: the system stalls even though progress was possible.
wait set: [Producer waiting for space, Consumer waiting for item]
state: buffer is FULL, an item was just added by... nobody yet
notify() -> wakes Producer -> still full -> re-waits
(the Consumer who could run was not woken) -> STALLWhen notify() is safe
You may use notify() only when all three hold:
- Uniform waiters — every thread waiting on the monitor is waiting for the same condition (so any one of them can validly proceed).
- One-in, one-out — a single
notifycorresponds to a single unit of progress (e.g. you produced exactly one item, so waking exactly one consumer is correct). - The waking is not relied upon to chain to a different kind of waiter.
A textbook fit is a Semaphore-like resource pool where every waiter wants "a permit" and you release exactly one.
Default to notifyAll()
notifyAll() is always correct (every thread re-checks its predicate and only the eligible ones proceed); the only cost is a thundering herd — all waiters wake, contend for the lock, and most go straight back to waiting, wasting CPU. For low contention this overhead is negligible, so the safe default is notifyAll().
// Producer/consumer on one monitor with two conditions:
// producers wait on "not full", consumers wait on "not empty"
// -> waiters are NOT uniform -> notify() can lose a wakeup
// -> use notifyAll()
notifyAll();
If notify()'s overhead actually matters, the cleaner fix is to stop sharing one monitor: use a ReentrantLock with separate Condition objects (notFull, notEmpty) and signal() the right one — that gives notify-level efficiency with notifyAll-level safety.