Friday, July 23, 2021

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

If you have used the wait() and notify() method in Java then you know that the standard idiom of calling the 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 the popular Effective Java book, a 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 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 conditions, but the programming world is not always ideal. In an ideal condition, your thread will only wake up if the 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, which could create an 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 threads will go for it and fill that spot but when another 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 the 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 these Java Multithreading courses 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. This 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 the 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, 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
50+ Java Multithreading Interview Questions in Java
How to avoid deadlock in Java programs
Top 5 Books to Learn Multithreading and Concurrency in Java

Thanks for reading this article so far. If you like this article then please share it 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

Feel free to comment, ask questions if you have any doubt.