The need for the loop is explained in the Javadoc for the wait
methods:
A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied.
To guard against this, after the wait()
call returns, you have to check the condition again, and if it's false, go back and call wait()
again instead of proceeding. The while
loop accomplishes that.
When you call wait()
, the object's lock is automatically released while waiting, and then acquired again before the method returns. This means that when another thread calls notify()
on the object, the waiting thread can't immediately resume running, because the notifying thread still holds the object's lock and the waiting thread has to wait for it to be released. It also means that if there are several waiting threads and you call notifyAll()
, the waiting threads can't all resume at once: one of the threads will get the lock and return from wait()
, and when it releases the lock, then another of the threads can acquire it and return from wait()
, and so on.
In some cases when multiple waiting threads are involved, a waiting thread may wake up, find that the condition is true, and do some stuff that ends up changing the condition back to false — all while holding the lock. Then, when it releases the lock (e.g. by calling wait()
again), the next thread wakes up and finds that the condition is false. In this case, it isn't a spurious wakeup; the condition really did become true, but then became false again before the thread got a chance to check it.
For example: a producer thread adds several items to a queue and calls notifyAll()
to wake up the consumer threads. Each consumer thread takes one item from the queue, then releases the lock while processing the item. But if there are more consumer threads than there were items added to the queue, some of the threads will wake up only to find that the queue is empty, so they just have to go back to waiting again.
Checking the condition in a while
loop takes care of this situation in addition to handling spurious wakeups.