Java concurrency is pretty complex topic and requires a lot of attention while writing application code dealing with multiple threads accessing one/more shared resources at any given time. Java 5, introduced some classes like BlockingQueue and Executors which take away some of the complexity by providing easy to use APIs.
Programmers using concurrency classes will feel a lot more confident than programmers directly handling synchronization stuff using wait(), notify() and notifyAll() method calls. I will also recommend to use these newer APIs over synchronization yourself, BUT many times we are required to do so for various reasons e.g. maintaining legacy code. A good knowledge around these methods will help you in such situation when arrived.
In this tutorial, I am discussing the purpose of wait() notify() notifyall() in Java. We will understand the difference between wait and notify.
Read more : Difference between wait() and sleep() in Java
1. What are wait(), notify() and notifyAll() methods?
The Object
class in Java has three final methods that allow threads to communicate about the locked status of a resource.
wait()
It tells the calling thread to give up the lock and go to sleep until some other thread enters the same monitor and calls
notify()
. Thewait()
method releases the lock prior to waiting and reacquires the lock prior to returning from thewait()
method. Thewait()
method is actually tightly integrated with the synchronization lock, using a feature not available directly from the synchronization mechanism.In other words, it is not possible for us to implement the
wait()
method purely in Java. It is a native method.General syntax for calling
wait()
method is like this:synchronized( lockObject ) { while( ! condition ) { lockObject.wait(); } //take the action here; }
notify()
It wakes up one single thread that called
wait()
on the same object. It should be noted that callingnotify()
does not actually give up a lock on a resource. It tells a waiting thread that that thread can wake up. However, the lock is not actually given up until the notifier’s synchronized block has completed.So, if a notifier calls
notify()
on a resource but the notifier still needs to perform 10 seconds of actions on the resource within its synchronized block, the thread that had been waiting will need to wait at least another additional 10 seconds for the notifier to release the lock on the object, even thoughnotify()
had been called.General syntax for calling
notify()
method is like this:synchronized(lockObject) { //establish_the_condition; lockObject.notify(); //any additional code if needed }
notifyAll()
It wakes up all the threads that called
wait()
on the same object. The highest priority thread will run first in most of the situation, though not guaranteed. Other things are same asnotify()
method above.General syntax for calling
notify()
method is like this:synchronized(lockObject) { establish_the_condition; lockObject.notifyAll(); }
wait()
method confirms that a condition does not exist (typically by checking a variable) and then calls the wait()
method. When another thread establishes the condition (typically by setting the same variable), it calls the notify()
method. The wait-and-notify mechanism does not specify what the specific condition/ variable value is. It is on developer’s hand to specify the condition to be checked before calling wait()
or notify()
.Let’s write a small program to understand how wait(), notify(), notifyall() methods should be used to get desired results.
2. How to use with wait(), notify() and notifyAll() methods
In this exercise, we will solve producer consumer problem using wait()
and notify()
methods. To keep program simple and to keep focus on usage of wait()
and notify()
methods, we will involve only one producer and one consumer thread.
Other features of the program are :
- Producer thread produce a new resource in every 1 second and put it in ‘taskQueue’.
- Consumer thread takes 1 seconds to process consumed resource from ‘taskQueue’.
- Max capacity of taskQueue is 5 i.e. maximum 5 resources can exist inside ‘taskQueue’ at any given time.
- Both threads run infinitely.
2.1. Producer Thread
Below is the code for producer thread based on our requirements :
class Producer implements Runnable { private final List<Integer> taskQueue; private final int MAX_CAPACITY; public Producer(List<Integer> sharedQueue, int size) { this.taskQueue = sharedQueue; this.MAX_CAPACITY = size; } @Override public void run() { int counter = 0; while (true) { try { produce(counter++); } catch (InterruptedException ex) { ex.printStackTrace(); } } } private void produce(int i) throws InterruptedException { synchronized (taskQueue) { while (taskQueue.size() == MAX_CAPACITY) { System.out.println("Queue is full " + Thread.currentThread().getName() + " is waiting , size: " + taskQueue.size()); taskQueue.wait(); } Thread.sleep(1000); taskQueue.add(i); System.out.println("Produced: " + i); taskQueue.notifyAll(); } } }
- Here “
produce(counter++)
” code has been written inside infinite loop so that producer keeps producing elements at regular interval. - We have written the
produce()
method code following the general guideline to writewait()
method as mentioned in first section. - Once the
wait()
is over, producer add an element in taskQueue and callednotifyAll()
method. Because the last-timewait()
method was called by consumer thread (that’s why producer is out of waiting state), consumer gets the notification. - Consumer thread after getting notification, if ready to consume the element as per written logic.
- Note that both threads use
sleep()
methods as well for simulating time delays in creating and consuming elements.
2.2. Consumer Thread
Below is the code for consumer thread based on our requirements :
class Consumer implements Runnable { private final List<Integer> taskQueue; public Consumer(List<Integer> sharedQueue) { this.taskQueue = sharedQueue; } @Override public void run() { while (true) { try { consume(); } catch (InterruptedException ex) { ex.printStackTrace(); } } } private void consume() throws InterruptedException { synchronized (taskQueue) { while (taskQueue.isEmpty()) { System.out.println("Queue is empty " + Thread.currentThread().getName() + " is waiting , size: " + taskQueue.size()); taskQueue.wait(); } Thread.sleep(1000); int i = (Integer) taskQueue.remove(0); System.out.println("Consumed: " + i); taskQueue.notifyAll(); } } }
- Here “
consume()
” code has been written inside infinite loop so that consumer keeps consuming elements whenever it finds something in taskQueue. - Once the
wait()
is over, consumer removes an element in taskQueue and callednotifyAll()
method. Because the last-time wait() method was called by producer thread (that’s why producer is in waiting state), producer gets the notification. - Producer thread after getting notification, if ready to produce the element as per written logic.
2.3. Test producer consumer example
Now lets test producer and consumer threads.
public class ProducerConsumerExampleWithWaitAndNotify { public static void main(String[] args) { List<Integer> taskQueue = new ArrayList<Integer>(); int MAX_CAPACITY = 5; Thread tProducer = new Thread(new Producer(taskQueue, MAX_CAPACITY), "Producer"); Thread tConsumer = new Thread(new Consumer(taskQueue), "Consumer"); tProducer.start(); tConsumer.start(); } }
Program Output.
Produced: 0 Consumed: 0 Queue is empty Consumer is waiting , size: 0 Produced: 1 Produced: 2 Consumed: 1 Consumed: 2 Queue is empty Consumer is waiting , size: 0 Produced: 3 Produced: 4 Consumed: 3 Produced: 5 Consumed: 4 Produced: 6 Consumed: 5 Consumed: 6 Queue is empty Consumer is waiting , size: 0 Produced: 7 Consumed: 7 Queue is empty Consumer is waiting , size: 0
I will suggest you to change the time taken by producer and consumer threads to different times, and check the different outputs in different scenario.
3. Interview questions on wait(), notify() and notifyAll() methods
3.1. What happens when notify() is called and no thread is waiting?
In general practice, this will not be the case in most scenarios if these methods are used correctly. Though if the notify()
method is called when no other thread is waiting, notify()
simply returns and the notification is lost.
Since the wait-and-notify mechanism does not know the condition about which it is sending notification, it assumes that a notification goes unheard if no thread is waiting. A thread that later executes the wait() method has to wait for another notification to occur.
3.2. Can there be a race condition during the period that the wait() method releases OR reacquires the lock?
The wait()
method is tightly integrated with the lock mechanism. The object lock is not actually freed until the waiting thread is already in a state in which it can receive notifications. It means only when thread state is changed such that it is able to receive notifications, lock is held. The system prevents any race conditions from occurring in this mechanism.
Similarly, system ensures that lock should be held by object completely before moving the thread out of waiting state.
3.3. If a thread receives a notification, is it guaranteed that the condition is set correctly?
Simply, no. Prior to calling the wait()
method, a thread should always test the condition while holding the synchronization lock. Upon returning from the wait()
method, the thread should always retest the condition to determine if it should wait again. This is because another thread can also test the condition and determine that a wait is not necessary — processing the valid data that was set by the notification thread.
This is a common case when multiple threads are involved in the notifications. More particularly, the threads that are processing the data can be thought of as consumers; they consume the data produced by other threads. There is no guarantee that when a consumer receives a notification that it has not been processed by another consumer.
As such, when a consumer wakes up, it cannot assume that the state it was waiting for is still valid. It may have been valid in the past, but the state may have been changed after the notify()
method was called and before the consumer thread woke up. Waiting threads must provide the option to check the state and to return back to a waiting state in case the notification has already been handled. This is why we always put calls to the wait() method in a loop.
3.4. What happens when more than one thread is waiting for notification? Which threads actually get the notification when the notify() method is called?
It depends on many factors.Java specification doesn’t define which thread gets notified. In runtime, which thread actually receives the notification varies based on several factors, including the implementation of the Java virtual machine and scheduling and timing issues during the execution of the program.
There is no way to determine, even on a single processor platform, which of multiple threads receives the notification.
Just like the notify()
method, the notifyAll()
method does not allow us to decide which thread gets the notification: they all get notified. When all the threads receive the notification, it is possible to work out a mechanism for the threads to choose among themselves which thread should continue and which thread(s) should call the wait()
method again.
3.5. Does the notifyAll() method really wake up all the threads?
Yes and no. All of the waiting threads wake up, but they still have to reacquire the object lock. So the threads do not run in parallel: they must each wait for the object lock to be freed. Thus, only one thread can run at a time, and only after the thread that called the notifyAll() method releases its lock.
3.6. Why would you want to wake up all of the threads if only one is going to execute at all?
There are a few reasons. For example, there might be more than one condition to wait for. Since we cannot control which thread gets the notification, it is entirely possible that a notification wakes up a thread that is waiting for an entirely different condition.
By waking up all the threads, we can design the program so that the threads decide among themselves which thread should execute next. Another option could be when producers generate data that can satisfy more than one consumer. Since it may be difficult to determine how many consumers can be satisfied with the notification, an option is to notify them all, allowing the consumers to sort it out among themselves.
Happy Learning !!
Hi Lokesh, How to terminate all those threads, after, let’s say, all 30 items where “produced” and “consumed”? Thank you.
Wonderful content and beautifully explained. thanks…
Threads are objects so they have lock like other objects. When we use wait() and notify() in synchronized methods, which lock do we release? Thread’s or the object sources which thread use?
@mimi, calling wait and notify release object’s lock. Thread don’t have lock associated with it, object’s have lock with it that’s why we call wait(), notify() method on object reference and not thread reference.
This is an excellent article. However, with the above code I get a very orderly production, until the queue is full, and a very orderly consumption, until the queue is empty, and then this repeats.
The example would be much better if the Thread.sleep() were placed outside the synchronized block, and randomized for a different timeout each time, say, between 500ms and 1000ms.
The critical section (synchronized block) is simply too large, and by the time the block ends, it repeats immediately and therefore the same thread is likely to reacquire the lock immediately, thus the long strings of production and consumption, without much interleaving. Move the sleep outside and randomize it, and you will have a much better example.
Thanks for the feedback
Thank you for the blog!
I have a question:
Why do we need to write `while (taskQueue.size() == MAX_CAPACITY)` ?
Can we use `if` instead of `while`?
because we want to check the condition repetitively, not only once.
Hi,
Thanks for such a wonderful post.
In your example both consumer and producer can’t run parallel right?
To produce or consume they require lock. So if producer is running, consumer can’t process any entry even if the queue is not empty.
Hello Lokesh,my problem is when i run the program post in your blog ,the output is regular,the consumer do not wake up until the queue is full,can you help me?tks
Produced: 0
Produced: 1
Produced: 2
Produced: 3
Produced: 4
Queue is full Producer is waiting , size: 5
Consumed: 0
Consumed: 1
Consumed: 2
Consumed: 3
Consumed: 4
Queue is empty Consumer is waiting , size: 0
Produced: 5
Produced: 6
Produced: 7
Produced: 8
Produced: 9
Queue is full Producer is waiting , size: 5
Consumed: 5
Consumed: 6
Consumed: 7
Consumed: 8
Consumed: 9
Queue is empty Consumer is waiting , size: 0
Produced: 10
Produced: 11
Produced: 12
Produced: 13
Produced: 14
Queue is full Producer is waiting , size: 5
Consumed: 10
Consumed: 11
Consumed: 12
Consumed: 13
Consumed: 14
Queue is empty Consumer is waiting , size: 0
Produced: 15
Produced: 16
Nobody can control the output, it’s totally depends on OS scheduler. Above output is also a valid output.
To have the process interleave at all I needed to add a Thread.currentThread().yield(); just inside the while (true) loops of the Consumer and Producer.
Hi Lokesh,
Can u tell me that what r the conditions to get the object lock. Means can we lock the object through synchronised method and what is the role of wait() method for getting the objects lock.
See i was really devasted and could not understand how to explain this concept in practical scenarios you showed how easily it can be explained
Hello Lokesh,
I implemented the code mentioned in the blog and mentioned below is the output I was getting
Now, the question :
Q.1
I hoped for this output only, not the output mentioned in the mentioned in blog as synchronization is on taskQueue (which is being passed from static void main() method) in both the class. So what happens when tProducer.start() happens. It gets the lock of taskQueue object and starts producing and then releases the lock when wait is called. At this time tConsumer which was was in waiting for the lock of taskQueue object in consume() method to get into synchronized block and goes into consuming the item from list and when size is zero it goes into wait and releases the lock and tProducer acquires it and this keeps on going. Am I correct with this understanding ?
Q.2 I am not able to get what does this “taskQueue.wait()” means in his code. What would have happened if I happen if I use “wait()” or “notifyAll()” only, when I tried it I got this as output after running for a while.
1) You are right.
2)
wait()
andnotifyAll()
must be called on some object, which producer and consumer both can access. So rather than creating a new monitor object for this purpose, I used this existing object.Regarding error, not sure how you have modified the wait() and notify() calls. Please share the modified code.
Hello Lokesh,
Firstly thanks for the clearing my doubt. And secondly, I just wrote like this in Producer and Consumer
Thanks for such a quick response and clearing my doubt.
and what I did in my code was “this.notifyAll()” and “this.wait()” INSTEAD of “taskQueue.wait()” or “taskQueue.notifyAll()”.
Now that you have told that “wait() and notifyAll() must be called on some object, which producer and consumer both can access” , I somehow think I am able to figure out why error came but it would be really nice if you can elaborate it or what this IllegalMonitorStateException means ?
You can’t wait() on an object unless the current thread owns that object’s monitor – otherwise
IllegalMonitorStateException
is thrown. To own the monitor of that object, you must synchronize on it.e.g. So if you want to use
this.wait()
which I do not recommend, you must do it insidesynchronized(this) {...}
block.Thanks for the post. It cleared up many of the confusions I had about threading in general. I just have one quick question about the notify() method. In Producer class after you add(produce) the product into the LinkedList you call notify(), is this to ‘wake up’ the Consumer Thread or Producer Thread? and if it’s for consumer Thread, why so ?
I only ask this question because my understanding of notify() is “it wakes up a thread that is waiting on this object’s moniotr”. Thus, since Producer has been put on “waiting” when the list is full, it has to be notified by notify()method within its body to start working again. Is this wrong?
when notify method is called which thread is revoked execution has two ambiguous answers.
1) definition of notify() : It wakes up the first thread that called wait() on the same object
2) answer of question: What happens when more than one thread is waiting for notification? Which threads actually get the notification when the notify() method is called?
Could you please tell me which one is correct.
Hi Chandan, Thanks for pointing out this typo. Second answer is correct. In first answer, please read “single” in place of “first”. I have updated the post.
Thanks Lokesh,
Article is really helpful to understand concepts of java multi threading thread communication. Is there some mechanism possible which can notify to a particular thread only or a particular thread should be invoked. ,Because I had a problem where have three threads t1,t2, t3 which are having data {1,4,7},{2,6,8},{3,6,9} respectively. I want to run these threads in parallel to give result of {1,2,3,4,5,6,7,8,9}
Regards,
Chandan
You may try this solution. https://stackoverflow.com/questions/30964133/3-threads-printing-numbers-in-sequence
Nice article.
It cleared many of my doubts regarding multithreading.
Like here in above code you associated wait() diirectly on taskqueue object.
https://www.java-samples.com/showtutorial.php?tutorialid=306
On this link,wait() is not written like this.wait(). Is wait() directly got link with q object.
Very Nice, Keep the Good Work .
I would like to have hyperlink on this from my website.
Thanks for kind words. And a hyperlink is much appreciated.
really good..most of other learning forums have explained the concept..but here you have put in very simple way. the approach of taking consumer from one Thread class object and Producer as different Thread class object simplified the things and I am able to understand quickly…keep posting..Thanks a lot…
HI,
Thanks for such an wonderful post. But recently, a question asked to my friend. So, can you help me with that.
1) Write a small program to create dead lock with wait() and notify().
2) Write a small programm to create dead lock with synchronixed block().
If possible please answer.
2) https://howtodoinjava.com/java/multi-threading/writing-a-deadlock-and-resolving-in-java/
1) I will work.
For #1
public class DeadLock {
public static void main(String[] args) throws InterruptedException {
new DeadLock().deadlock();
}
private synchronized void deadlock() {
try {
wait();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
But the problem is it’s only with wait(). Not with using wait() notify both. So, it will be great if you can provide exaple with both.
Thanks for the #2 answer
“By waking up all the threads, we can design the program so that the threads decide among themselves which thread should execute next”
Can you please explain me how it can be done ? How can the threads decide among themselves ?
An example could be group of threads (one thread processing only a specific type of message) watching a message queue. Upon placing a message in queue, producer can notify all consumer threads. Then each consumer thread may check if message is for it, and if it is then process it otherwise wait again.
This design may look inefficient as correct thread may get chance to check in last; BUT it will start making sense when you start adding more message types and their handler threads into system without modifying code of other handlers.
Just for an example. Make sense?
yes..Thanks
I think in this example instead of List, Queue should be used.
Else each time only element @ location zero will be consumed only.
I appreciate the article very much!
So I’d like to argue for it. Maybe a queue is better to understand here, but there is no doubt that a list can be used as a queue in a proper way (append to rear, get&remove from front), as what the article did. For your question, a consumer indeed consumes only element @location zero each time, and this is also the meaning of First Out for a queue.