Spurious Wakeup in Java
Fast recall
Quick revision
Core answer
wait() even though no notification, interruption, or timeout explains it. Java requires waiting code to recheck its condition in a while loop. The loop is also necessary because another awakened thread may make the condition false before this thread reacquires the monitor.Knowledge of a subtle but important threading edge case that can cause rare production bugs.
Using if instead of while to check conditions — code works in most tests but fails in rare production scenarios.
Show the correct while-loop pattern in code and explain why the JVM spec allows spurious wakeups as a performance optimization.
Interview-ready guide
Interview answer
A spurious wakeup means wait() returns even though no normal wake-up cause explains it. Java permits this, so a thread must treat notification as a hint to check its condition, not as proof that it can proceed.
The correct pattern is while (!condition) { lock.wait(); }. The thread checks while holding the monitor, waits, reacquires the same monitor, and checks again. That loop is also necessary with notifyAll(), because another awakened thread may acquire the monitor first and consume the available item. I would base the answer on the Java API rule rather than claim all JVMs behave this way for one POSIX implementation reason.
Spurious Wakeup in Java
Direct Answer
A spurious wakeup is a permitted return from wait() even though no notification, interruption, or timeout explains it. Java requires waiting code to recheck its condition in a while loop. The loop is also necessary because another awakened thread may make the condition false before this thread reacquires the monitor.
Code Example
import java.util.LinkedList;
import java.util.Queue;
class SafeQueue<T> {
private final Queue<T> items = new LinkedList<>();
private final int capacity;
public SafeQueue(int capacity) {
this.capacity = capacity;
}
public synchronized void put(T item) throws InterruptedException {
while (items.size() >= capacity) { // WHILE not IF
wait(); // may wake spuriously
}
// When we reach here, we KNOW size < capacity
items.add(item);
notifyAll();
}
public synchronized T take() throws InterruptedException {
while (items.isEmpty()) { // WHILE not IF
wait(); // may wake spuriously or due to another take()
}
// When we reach here, we KNOW the queue is non-empty
var item = items.poll();
notifyAll();
return item;
}
}
Common Mistakes & Pitfalls
- Using
ifaround wait: one wakeup does not prove the condition is true. - Explaining the loop only with spurious wakeups: competing waiters can also change the condition before this thread reacquires the monitor.
- Testing the condition outside the monitor: the check and transition into the wait set must use the same locking protocol.
- Assuming a notification is stored: a notify call with no waiter does not become a future permit.
- Depending on rarity: correctness follows the API contract, not how often a wakeup pattern appears in tests.
Check your understanding
Answer these without looking back at the article.
- Does the Java specification allow spurious wakeups?
- What is the producer-consumer pattern?
- How does java.util.concurrent.BlockingQueue eliminate the need for manual wait/notify?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.