What wait() really does
Inside synchronized (lock), calling lock.wait():
- Releases the monitor of
lock. - Puts the current thread into waiting state.
- Later, after
notify()/notifyAll()(or interrupt/timeout), the thread must re-acquire the same lock. - Execution continues right after
wait().
So it is not a real “jump out of the synchronized block”.
It is: pause at wait(), temporarily give up the lock, then continue after re-locking.
synchronized (lock) {
System.out.println("before wait");
lock.wait();
System.out.println("after wait");
}What notify() / notifyAll() really do
Inside synchronized (lock), calling lock.notify() or lock.notifyAll():
- Does not release the lock immediately.
- Marks one (arbitrary) waiting thread (
notify) or all waiting threads (notifyAll) onlockas candidates to wake up. - Those awakened threads cannot continue immediately, because the current thread still owns the monitor.
- Only after the current thread exits the
synchronizedblock (or otherwise releases the monitor), can a woken thread compete to re-acquire the same lock. - After re-acquiring the lock, the awakened thread continues from right after its
wait()call.
Why wait() must be in while, not if
wait() returning does not guarantee the condition is true.
A wake-up can happen because of:
- spurious wakeups
notifyAll()waking multiple threads- state changes by another thread before this thread re-acquires the lock
Correct pattern:
synchronized (lock) {
while (!condition) {
lock.wait();
}
// safe: condition is true here
}Not safe:
synchronized (lock) {
if (!condition) {
lock.wait();
}
// condition may be false again
}Contrast with sleep()
wait(): releases the lock.sleep(): does not release the lock.