Why should wait() always be called inside a loop

Viewed 60807

I have read that we should always call a wait() from within a loop:

while (!condition) { obj.wait(); }

It works fine without a loop so why is that?

11 Answers

You need not only to loop it but check your condition in the loop. Java does not guarantee that your thread will be woken up only by a notify()/notifyAll() call or the right notify()/notifyAll() call at all. Because of this property the loop-less version might work on your development environment and fail on the production environment unexpectedly.

For example, you are waiting for something:

synchronized (theObjectYouAreWaitingOn) {
   while (!carryOn) {
      theObjectYouAreWaitingOn.wait();
   }
}

An evil thread comes along and:

theObjectYouAreWaitingOn.notifyAll();

If the evil thread does not/can not mess with the carryOn you just continue to wait for the proper client.

Edit: Added some more samples. The wait can be interrupted. It throws InterruptedException and you might need to wrap the wait in a try-catch. Depending on your business needs, you can exit or suppress the exception and continue waiting.

It's answered in documentation for Object.wait(long milis)

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. In other words, waits should always occur in loops, like this one:

 synchronized (obj) {
     while (<condition does not hold>)
         obj.wait(timeout);
     ... // Perform action appropriate to condition
 }

(For more information on this topic, see Section 3.2.3 in Doug Lea's "Concurrent Programming in Java (Second Edition)" (Addison-Wesley, 2000), or Item 50 in Joshua Bloch's "Effective Java Programming Language Guide" (Addison-Wesley, 2001).

Why should wait() always be called inside a loop

The primary reason why while loops are so important is race conditions between threads. Certainly spurious wakeups are real and for certain architectures they are common, but race conditions are a much more likely reason for the while loop.

For example:

synchronized (queue) {
    // this needs to be while
    while (queue.isEmpty()) {
       queue.wait();
    }
    queue.remove();
}

With the above code, there may be 2 consumer threads. When the producer locks the queue to add to it, consumer #1 may be blocked at the synchronized lock while consumer #2 is waiting on the queue. When the item is added to the queue and notify called by the producer, #2 is moved from the wait queue to be blocked on the queue lock, but it will be behind the #1 consumer which was already blocked on the lock. This means that the #1 consumer gets to go forward first to call remove() from the queue. If the while loop is just an if, then when consumer #2 gets the lock after #1 and calls remove(), an exception would occur because the queue is now empty -- the other consumer thread already removed the item. Even though it was notified, it needs to be make sure the queue is for sure not empty because of this race condition.

This well documented. Here's a web page I created a while back which explains the race condition in detail and has some sample code.

There might be more then just one worker waiting for a condition to become true.

If two or more worker get awake (notifyAll) they have to check the condition again. otherwise all workers would continue even though there might only be data for one of them.

Before getting to the answer, lets see how wait is probably implemented.

wait(mutex) {
   // automatically release mutex
   // and go on wait queue

   // ... wait ... wait ... wait ...

   // remove from queue
   // re-acquire mutex
   // exit the wait operation
}

In your example mutex is the obj with the assumption that your code is running inside synchronized(obj) { } block.

A mutex is called as monitor in Java [some subtle differences though]

A concurrency example using condition variable with if

synchronized(obj) {
  if (!condition) { 
    obj.wait(); 
  }
  // Do some stuff related to condition
  condition = false;
}

Lets say we have 2 threads. Thread 1 and Thread 2. Lets see some states along the timeline.

at t = x

Thread 1 state:

waiting on ... wait ... wait ... wait ..

Thread 2 state:

Just entered the synchronised section, since as per the thread 1's state, the mutex/monitor is released.

You can read more about wait() here java.sun.com/javase/6/docs/api/java/lang/Object.html#wait(long).

This is the only thing that is tricky to understand. When 1 thread is inside the synchronized block. Another thread can still enter the synchronized block because wait() causes the monitor/mutex to be released.

Thread 2 is about to read if (!condition) statement.

at t = x + 1

notify() is triggered by some thread on this mutex/monitor.

condition becomes true

Thread 1 state:

Waiting at re-acquire mutex, [Since thread-2 has the lock now]

Thread 2 state:

Doesn't go inside if condition and marks the condition = false.

at t = x + 2

Thread 1 state:

Exits the wait operation and about to mark condition = false.

This state is inconsistent as condition is supposed to be true but is false already, because thread 2 marked it false previously.

And thats the reason, while is required instead of if. As while would trigger the condition to be checked again for thread 1 and thread 1 will begin waiting again.

Result

In order to avoid this inconsistency the correct code seems to be like this:

synchronized(obj) {
  while (!condition) { 
    obj.wait(); 
  }
  // Do some stuff related to condition
  condition = false;
}

Three things you will see people do:

  • Using wait without checking anything (BROKEN)

  • Using wait with a condition, using an if check first (BROKEN).

  • Using wait in a loop, where the loop test checks the condition (NOT BROKEN).

Not appreciating these details about how wait and notify work leads people to choose the wrong approach:

  • One is that a thread doesn't remember notifications that happened before it got around to waiting. The notify and notifyAll methods only effect threads that are already waiting, if a thread isn't waiting at the time it is out of luck.

  • Another is that a thread releases the lock once it starts waiting. Once it gets a notification it re-acquires the lock and continues on where it left off. Releasing the lock means that thread does not know anything about the current state once it wakes back up, any number of other threads could have made changes since then. The check made before the thread started waiting doesn't tell you anything about what the state is currently.

So the first case, with no checking, makes your code vulnerable to race conditions. It might happen to work by accident if one thread has enough of a head start over another. Or you may have threads waiting forever. If you sprinkle in timeouts then you end up with slow code that sometimes doesn't do what you want.

Adding a condition to check apart from the notification itself protects your code from these race conditions and gives your code a way to know what the state is even if the thread wasn't waiting at the right time.

The second case, with if-checks, is likely to work if you have only 2 threads. That puts a limit on the number of states things can get into and when you made faulty assumptions you don't get burned so badly. This is the situation for lots of toy example code exercises. The result is people come away thinking they understand, when they really don't.

Protip: Real world code has more than two threads.

Using the loop lets you re-check the condition once you re-acquire the lock so that you're moving forward based on current state, not on stale state.

In simple words,

'if' is a conditional statement , once condition is satisfied remaining block of code will get executed.

'while' is a loop which going check the condition unless condition is not satisfied.

Related