Of course! This is a very common and important question in Java concurrency. The short answer is:

You do not use wait() directly inside an if statement. You must use it inside a while loop.
Let's break down why this is so critical and how to do it correctly.
The Golden Rule of wait()
The fundamental rule for using wait() is:
Always call
wait()inside a loop that checks the condition it is waiting for.(图片来源网络,侵删)
The "Why": Spurious Wakeups
The primary reason for this rule is that a thread can be woken up from wait() for reasons other than the intended notify() or notifyAll(). These are called spurious wakeups.
According to the Java Language Specification (JLS), wait() can return "without any notification having been received." This is a rare, but possible, event. If your code relies on an if statement, a spurious wakeup will cause the thread to proceed incorrectly, assuming the condition has been met when it hasn't.
Example of INCORRECT Code (using if)
Imagine a simple scenario where a producer adds an item to a queue, and a consumer waits for an item to be available.
// INCORRECT - DO NOT USE
class Queue {
private String item;
private boolean itemAvailable = false;
// Called by the Producer
public void put(String item) {
this.item = item;
this.itemAvailable = true;
System.out.println("Producer: Notifying a consumer.");
notify(); // Wake up one waiting thread
}
// Called by the Consumer
public String get() throws InterruptedException {
// THE PROBLEM IS HERE
if (!itemAvailable) { // <--- IF STATEMENT
System.out.println("Consumer: Waiting for an item...");
wait(); // This could return spuriously!
}
// If we get here, we assume itemAvailable is true
String theItem = this.item;
this.itemAvailable = false;
return theItem;
}
}
What could go wrong?

- Consumer calls
get(): TheitemAvailableisfalse. The consumer callswait()and goes to sleep. - Producer calls
put(): The producer adds an item, setsitemAvailabletotrue, and callsnotify(). This wakes up the consumer. - Consumer resumes: The consumer's lock is reacquired, and execution continues after the
wait()call. It gets the item and everything works fine.
Now, let's introduce a spurious wakeup:
- Consumer calls
get(): TheitemAvailableisfalse. The consumer callswait()and goes to sleep. - Spurious Wakeup: For an unknown reason, the consumer's thread is woken up by the JVM, before the producer has even called
put(). - Consumer resumes: The consumer's lock is reacquired, and execution continues after the
wait()call. - The Bug: The
ifcondition (!itemAvailable) is still true! The code completely ignores this and proceeds to try and accessthis.item, which isnull. This will likely result in aNullPointerExceptionor incorrect behavior.
The Correct Way: Using a while Loop
To solve the spurious wakeup problem and handle other race conditions, you must re-check the condition inside a while loop after wait() returns.
Example of CORRECT Code (using while)
Here is the same Queue class, implemented correctly.
// CORRECT - ALWAYS USE A WHILE LOOP
class CorrectQueue {
private String item;
private boolean itemAvailable = false;
// Called by the Producer
public synchronized void put(String item) {
// If an item is already available, wait until the consumer takes it
while (itemAvailable) {
try {
wait(); // Producer can also wait if the queue is full
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
this.item = item;
this.itemAvailable = true;
System.out.println("Producer: Produced '" + item + "'. Notifying consumer.");
notify(); // Wake up one waiting thread (consumer or producer)
}
// Called by the Consumer
public synchronized String get() throws InterruptedException {
// THE FIX IS HERE - A WHILE LOOP
while (!itemAvailable) { // <--- WHILE LOOP
System.out.println("Consumer: Waiting for an item...");
wait(); // This will re-check the condition after being notified or spuriously woken up
}
// If we get here, we are GUARANTEED that itemAvailable is true
String theItem = this.item;
this.itemAvailable = false;
System.out.println("Consumer: Consumed '" + theItem + "'. Notifying producer.");
notify(); // Wake up the producer in case it was waiting
return theItem;
}
}
Why this works:
- Consumer calls
get():itemAvailableisfalse. The consumer enters thewhileloop and callswait(). - Spurious Wakeup: The consumer's thread is woken up spuriously.
- Consumer re-enters the loop: The lock is reacquired, and the
whilecondition is re-evaluated.!itemAvailableis stilltrue.- The condition is met, so the thread calls
wait()again and goes back to sleep.
- Producer calls
put(): The producer setsitemAvailabletotrueand callsnotify(). This wakes up the consumer. - Consumer re-enters the loop (again): The lock is reacquired, and the
whilecondition is re-evaluated.!itemAvailableis nowfalse.- The condition is not met, so the loop exits.
- Safe Execution: The code proceeds to safely get the item. The condition has been verified one last time right before the critical section.
Summary: if vs. while for wait()
| Feature | if Statement |
while Loop |
|---|---|---|
| Purpose | A one-time check. Assumes the condition is true after waking up. | A continuous re-check. Ensures the condition is still true after waking up. |
| Handles Spurious Wakeups? | No. A spurious wakeup will cause the thread to proceed incorrectly. | Yes. The thread will re-check the condition and, if it's still false, call wait() again. |
| Best Practice | Never use wait() with an if. |
Always use wait() with a while loop. |
| Common Analogy | "Wake me up at 7 AM." (If you wake up at 3 AM, you get up anyway). | "Wake me up for work, but only if it's after 7 AM." (If you wake up at 3 AM, you go back to sleep). |
Key Takeaways
- Synchronization is Required: The thread that calls
wait()must own the object's lock. This meanswait()andnotify()/notifyAll()must be called from within asynchronizedmethod or block. - Always Use
while: Protect yourself from spurious wakeups and race conditions by always checking the waiting condition in awhileloop. - Check the Condition: The condition in the
whileloop should be the exact opposite of the condition that made you callwait()in the first place.

