wait() and notify() Inside synchronized Block
Fast recall
Quick revision
Core answer
wait, notify, and notifyAll operate on one object's monitor and wait set, so the caller must own that monitor or Java throws IllegalMonitorStateException. Holding the same monitor also lets code check a condition and enter the wait set without a notifier changing that condition in between. This works only when every participant guards the condition with that same monitor.Deeper understanding of the monitor-based synchronization model and why the synchronized requirement prevents race conditions.
Not knowing that calling wait outside synchronized throws IllegalMonitorStateException at runtime.
Explain the race condition that would occur without the synchronized requirement — a thread could miss a notification between checking the condition and calling wait.
Interview-ready guide
Interview answer
The reason wait and notify must be called inside synchronized is all about preventing a race condition.
Let me explain. When you want to wait for something, you first check a condition. If the condition's not met, you call wait.
But imagine there's no synchronized requirement. You check the condition, find it's false, and decide to wait. Right in that tiny moment between your check and your wait call, another thread could come in, change the condition to true, and call notify.
You'd miss that notification completely and end up waiting forever. That's called a lost notification, and it's a nasty bug. By requiring synchronized, Java makes sure that checking the condition and calling wait happen atomically — no other thread can sneak in and change things while you're in the middle.
The synchronized keyword means you hold the monitor, which is the lock associated with that object. " If you don't own the monitor, there's nothing to release, so the JVM throws IllegalMonitorStateException. Same thing with notify — you're modifying the waiting set for that object, so you need to own the monitor.
This design prevents the race condition entirely. When you wake up from wait, you automatically reacquire the monitor before the method returns, which forces you to recheck the condition safely. It's a really elegant solution to a hard synchronization problem.
wait() and notify() Inside synchronized Block
Direct Answer
wait, notify, and notifyAll operate on one object's monitor and wait set, so the caller must own that monitor or Java throws IllegalMonitorStateException. Holding the same monitor also lets code check a condition and enter the wait set without a notifier changing that condition in between. This works only when every participant guards the condition with that same monitor.
Code Example
class BrokenCounter {
private int count = 0;
// WRONG: checking and waiting are not atomic
public void waitForNonZero() throws InterruptedException {
if (count == 0) {
// Another thread could increment and notify HERE
// Then this wait() would miss that notification
wait(); // Also throws IllegalMonitorStateException!
}
}
// CORRECT: synchronized makes check and wait atomic
public synchronized void waitForNonZeroCorrect() throws InterruptedException {
while (count == 0) {
wait(); // atomically releases monitor and waits
}
// When we wake up, we still hold the lock
System.out.println("Count is now: " + count);
}
public synchronized void increment() {
count++;
notifyAll(); // must hold monitor to notify
}
}
Common Mistakes & Pitfalls
- Forgetting synchronized entirely: Freshers write code that calls wait() or notify() in a regular method, not realizing this throws IllegalMonitorStateException at runtime. The code compiles fine but crashes when executed.
- Synchronizing on the wrong object: If you synchronize on lockA but call wait on lockB, you'll still get IllegalMonitorStateException because you don't own lockB's monitor. The synchronized object and the wait/notify object must be the same.
- Thinking wait releases all locks: wait() only releases the monitor of the specific object it's called on. If a thread holds multiple locks, wait() releases just one — this can cause deadlocks if not understood correctly.
Check your understanding
Answer these without looking back at the article.
- What is a missed notification (missed wakeup)?
- What happens if notify() is called when no thread is waiting?
- How does ReentrantLock.Condition solve the same problem more flexibly?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.