skip to content

wait, notify, notifyAll

wait releases the monitor and parks the thread, notify and notifyAll wake waiters, and all three require you to already hold the lock. The two details interviewers insist on are the guarded loop (because of spurious wakeups) and why notifyAll is usually the safe choice.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What do wait(), notify(), and notifyAll() do, and what precondition must hold whenever you call any of them?

level: middleimportance: must knowfreq 78%

answer

  1. Methods live on Object, not Thread
  2. Must hold the monitor → else IllegalMonitorStateException
  3. wait releases the lock and re-acquires on return
  4. Always wait in a while loop (spurious wakeups)
  5. notifyAll = safe default; notify = one arbitrary waiter

basics

~20 s

wait() makes the current thread pause and release the object's lock until another thread calls notify() or notifyAll() on the same object. notify() wakes one waiting thread; notifyAll() wakes them all. You must hold that object's lock (be inside a synchronized block on it) when you call any of the three, or you get IllegalMonitorStateException.

solid answer

~40 s

These three methods live on java.lang.Object and implement the monitor's wait-set: a place threads park until a condition becomes true. wait() atomically releases the object's intrinsic lock and suspends the calling thread, adding it to that object's wait-set; when later woken it re-acquires the lock before returning. notify() moves one arbitrary thread from the wait-set to runnable; notifyAll() moves all of them. All three require the caller to already hold the object's monitor (be inside a synchronized block/method on that same object), otherwise the JVM throws IllegalMonitorStateException. Because a thread can wake without anyone signaling (spurious wakeup) and because the condition may have changed between notify and re-acquisition, wait() must always be called inside a while loop that re-checks the guard condition, never an if.

go deeper

for a junior

Knows wait() pauses a thread and notify() wakes it, and that they're used for thread coordination. May not recall the lock-ownership rule or the loop requirement.

for a middle

States all three are Object methods requiring monitor ownership, that wait() releases the lock, and that wait must sit in a while loop because of spurious wakeups.

for a senior

Explains the atomic release-and-park, re-acquisition on return, the lost-wakeup race the synchronized requirement prevents, and chooses notifyAll vs notify deliberately.

for a principal

Frames wait/notify as the primitive under higher-level java.util.concurrent tools, discusses fairness/thundering-herd trade-offs, and steers teams toward BlockingQueue or Lock/Condition for maintainability.

## The problem these solve Sometimes a thread cannot make progress until some *condition* becomes true — e.g. a consumer cannot take from an empty queue until a producer adds an item. Busy-waiting (looping and re-checking while holding CPU) wastes cycles. Java provides a way for a thread to **block until notified**, releasing the CPU in the meantime. This is the `wait`/`notify`/`notifyAll` mechanism. ## Key terms, defined from scratch - **Monitor / intrinsic lock:** every Java object has an associated lock (a 'monitor'). Entering a `synchronized` block on an object acquires that object's monitor; leaving releases it. Only one thread can hold a given monitor at a time. - **Wait-set:** each monitor also has an associated *wait-set* — a holding area of threads that have called `wait()` on that object and are parked, not competing for the lock. - **These three methods are on `java.lang.Object`**, not Thread, because the lock and wait-set belong to *objects*. Any object can be used as a condition variable. ## What each call does - **`obj.wait()`**: (1) verifies the current thread holds `obj`'s monitor; (2) **atomically releases** that monitor and suspends the thread, placing it in `obj`'s wait-set. When the thread is later woken, (3) it must **re-acquire** `obj`'s monitor before `wait()` returns — so on return it again holds the lock. There are overloads `wait(long timeoutMillis)` and `wait(long, int nanos)` that also wake after the timeout elapses. - **`obj.notify()`**: moves **one** arbitrary thread from `obj`'s wait-set to the runnable state (it still has to re-acquire the lock before proceeding). Which one is unspecified. - **`obj.notifyAll()`**: moves **all** threads in `obj`'s wait-set to runnable; they then contend for the lock one at a time. ## The mandatory precondition You must **own `obj`'s monitor** when calling any of the three — i.e. be inside `synchronized(obj){ ... }` (or a synchronized instance method calling `this.wait()`, or a synchronized static method using the Class object). If you call them without holding that exact monitor, the JVM throws `IllegalMonitorStateException` at runtime. This rule exists because the check-condition-then-wait sequence must be atomic: you check the guard *while holding the lock*, and `wait()` releases it atomically so no signal can slip in between the check and the park (the 'lost wakeup' it prevents). ## Spurious wakeups and the guarded-loop rule A thread in `wait()` may return **without** any `notify` having occurred — a *spurious wakeup*, permitted by the JVM/OS for implementation reasons. Also, by the time a notified thread re-acquires the lock, another thread may have already consumed the condition. Therefore the condition must be **re-checked in a loop**: ```java synchronized (lock) { while (!conditionHolds()) { // while, never if lock.wait(); } // condition is true AND we hold the lock } ``` Using `if` here is the classic bug: a spurious wakeup or a stale notify lets the thread proceed with the condition false. ## notify vs notifyAll `notify()` wakes one waiter and is cheaper, but is only safe when **all** waiters are interchangeable and waiting on the **same** condition — otherwise you can wake the 'wrong' thread (one whose condition is still false), which goes back to sleep while the thread that *could* proceed is never woken (a stuck system). `notifyAll()` is the safe default: every waiter wakes, re-checks its guard, and only the eligible ones proceed; the rest loop back to `wait()`. The cost is a 'thundering herd' of wakeups contending for the lock. ## Why it's largely legacy From `java.util.concurrent` you usually prefer higher-level tools: `BlockingQueue` for producer-consumer, `CountDownLatch`/`CyclicBarrier`/`Semaphore` for coordination, or `Lock` + `Condition` (`await`/`signal`/`signalAll`) which gives multiple named wait-sets per lock. But `wait`/`notify` remains the foundation interviewers probe.

  • Why are these methods on Object rather than Thread?
    Because the lock and wait-set are properties of the object being used as the monitor/condition variable, not of a thread. Any object can serve as a condition, so each object carries its own wait-set.
  • What exactly throws IllegalMonitorStateException?
    Calling wait(), notify(), or notifyAll() on an object whose monitor the current thread does not hold — i.e. outside a synchronized block/method on that same object.

saying these in an interview costs you the question

  • Saying wait/notify are Thread methods
  • Calling wait() inside an if instead of a while
  • Believing wait() keeps holding the lock while parked (it releases it)
  • Claiming notify() lets you choose which thread wakes
  • Calling wait/notify outside any synchronized block and expecting it to work

context

open as a page

What is a spurious wakeup, and what coding discipline does it force on every use of wait()?

level: middleimportance: must knowfreq 64%

basics

~20 s

A spurious wakeup is when a thread returns from wait() even though nobody called notify() or notifyAll(). Because it can happen, you must always call wait() inside a while loop that re-checks the condition, so the thread only proceeds when the condition is actually true — never inside a plain if.

open as a page

Implement a bounded producer-consumer buffer using wait/notify. Why does wait() have to release the lock for this to work?

level: seniorimportance: must knowfreq 70%

basics

~20 s

A producer adds items and a consumer removes them, sharing a fixed-size buffer guarded by one lock. The producer waits when the buffer is full; the consumer waits when it's empty; each notifies the other after changing the buffer. wait() must release the lock so the other thread can enter the synchronized region and make progress.

open as a page

When is it safe to use notify() instead of notifyAll(), and what bug appears if you pick wrong?

level: seniorimportance: should knowfreq 58%

basics

~20 s

notify() wakes one waiting thread; notifyAll() wakes all of them. notify() is only safe when every waiting thread is waiting for the exact same condition and any of them can proceed interchangeably. If different threads wait for different conditions, notify() might wake the 'wrong' one, which goes back to sleep, leaving the thread that could actually run never woken — the program stalls.

open as a page

How do Lock/Condition (await/signal/signalAll) improve on intrinsic wait/notify, and what does each map to?

level: principalimportance: should knowfreq 47%

basics

~20 s

ReentrantLock with Condition objects is the modern replacement for synchronized + wait/notify. await() maps to wait(), signal() to notify(), and signalAll() to notifyAll(). The big win is that one Lock can have several Conditions (several wait-sets), so you can wake exactly the right group of threads instead of waking everyone.

open as a page