Why wait() and notify() method should be called inside a loop in Java?

If you have used wait() and notify() method in Java then you know that the standard idiom of calling wait() method uses a loop, but have you ever thought why? This is even advised by none other than Joshua Bloch, a Java guru and author of popular Effective Java book, must read for any Java programmer. When I first started using this method, I was puzzled why not just use the if block because ultimately we are testing for a condition and then either waiting or going for further processing. An if block is more readable for the testing condition than a while loop like for the classic producer-consumer problem, the waiting condition for producer thread could be written as :

if(queue.isFull()){
 
  queue.wait()
 
}
 
queue.add(item);

This says if the queue is full then wait, otherwise insert an element into the queue. This looks fine at first sight but when you think through about this in a multi-threaded scenario, you will spot the problem and that's what you will learn in this article.

 Btw, this is my second article on exploring how to use the wait-notify method in Java. Earlier I have written about why wait and notify is called from the synchronized context and today you'll learn why a loop is necessary for the wait-notify block. If you haven't read that article, you may find it interesting and useful.




What is the problem using if block with wait-notify in Java?

This works fine in ideal condition, but the programming world is not always ideal. In an ideal condition, your thread will only wake up if consumer thread has consumed one element and now the queue is not full, but in the real world, the waiting thread can be woken up even if the condition has not changed like an evil thread sends an incorrect notification.

If you use the if block then you are not going to check the waiting condition again before start processing or adding another item in the queue. In that case, you end up putting one more element in the queue which is full, that could create error or exception.

This is why you should always check the waiting condition in a loop instead of if block.

There is another scenario, where multiple producers are waiting for a spot in the queue. If a user called notifyAll() and informed all waiting threads about the one spot being available.

After receiving this notification, one of the thread will go for it and fill that spot but when other thread gets wake-up the spot is already filled. If you don't check the condition in the loop then you end up adding more elements in the already full queue.

When you check the waiting condition in the loop you ensure that thread will test the condition after it wakes up to see if the condition still holds or not. If you are not familiar with notify and notifyAll method, you can further see The Complete Java Masterclass to learn more.

Why wait() and notify() method should be called from loop in Java?



Reasons due to which a thread can wake up in Java

Misdelivered notification: 
The order in which Java threads execute after receipt of a notifyAll() signal is unspecified. Which means it's possible that an unrelated thread could start executing and discover that its condition predicate is satisfied. Consequently, it could resume execution despite being required to remain dormant.

Spurious wakeups: 
Certain Java Virtual Machine (JVM) implementations are vulnerable to spurious wakeups that result in waiting threads waking up even without a notification

This is also true for await() method which is a new method for Condition class but similar to wait().


The wrong way to use the wait() method


synchronized (object) {
 
  if (condition does not hold) {
 
     object.wait();
 
  }
 
  // Proceed when condition holds
 
}


The right way to use the wait() method:

synchronized (object) {
 
   while (condition does not hold) {
 
     object.wait();
 
   }
 
   // Proceed when condition holds
 
}


That's all about why wait() method should be called inside the loop in Java instead of if block. As I have said before, even the Java guru, Joshua Bloch, who has written many key classes of the java.lang package has advised this in his classic Effective Java book, a must read for any serious Java programmer. It will help you to avoid any such mistakes in your day-to-day programming task.

Further Learning
The Complete Java Masterclass
Applying Concurrency and Multi-threading to Common Java Patterns
Java Concurrency in Practice - The book
Java Concurrency in Practice Course by Heinz Kabutz
50+ Java Multithreading Interview Questions in Java
How to avoid deadlock in Java programs
Top 5 Courses to Learn Multithreading and Concurrency in Java
Multithreading and Parallel Computing in Java

Thanks for reading this article so far. If you like this article then please share with your friends and colleagues. If you have any questions or feedback then please share with your friends and colleagues.

No comments:

Post a Comment