I am confused a bit about wait and notify/notifyAll.
I know there is a lock for every java object. I know wait will release the lock for other thread. How about noti
No -- notify
/notifyAll
don't release locks like wait
does. The awakened thread can't run until the code which called notify
releases its lock.
This is what the Javadoc says:
The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.
To clarify my understanding and to provide an example for all to show when the lock is released, I have added print statements to the following code after the call to notify()/NotifyAll():
class ThreadDemo {
public static void main(String[] args) {
Shared s = new Shared();
new Producer(s).start();
new Consumer(s).start();
}
}
class Shared {
private char c = '\u0000';
private boolean writeable = true;
synchronized void setSharedChar(char c) {
while (!writeable)
try {
wait();
} catch (InterruptedException e) {
}
this.c = c;
writeable = false;
notifyAll();
System.out.println("setSharedChar notify() called - still in synchronized block.");
}
synchronized char getSharedChar() {
while (writeable)
try {
wait();
} catch (InterruptedException e) {
}
writeable = true;
notifyAll();
System.out.println("getSharedChar notify() called - still in synchronized block.");
return c;
}
}
class Producer extends Thread {
private Shared s;
Producer(Shared s) {
this.s = s;
}
public void run() {
System.out.println("Starting producer thread.");
for (char ch = 'A'; ch <= 'Z'; ch++) {
System.out.println("Producer thread getting ready to create a char.");
try {
Thread.sleep((int) (Math.random() * 1000));
} catch (InterruptedException e) {
}
s.setSharedChar(ch);
System.out.println(ch + " produced by producer.");
}
}
}
class Consumer extends Thread {
private Shared s;
Consumer(Shared s) {
this.s = s;
}
public void run() {
System.out.println("Starting consumer thread.");
char ch;
do {
System.out.println("Consumer thread getting ready to read a char.");
try {
Thread.sleep((int) (Math.random() * 1000));
} catch (InterruptedException e) {
}
ch = s.getSharedChar();
System.out.println(ch + " consumed by consumer.");
} while (ch != 'Z');
}
}
When I ran this example enough times, there was a point where I did eventually see the output of the program show:
...
F produced by producer.
Producer thread getting ready to create a char.
getSharedChar notify() called - still in synchronized block.
F consumed by consumer.
Consumer thread getting ready to read a char.
setSharedChar notify() called - still in synchronized block.
G produced by producer.
Producer thread getting ready to create a char.
getSharedChar notify() called - still in synchronized block.
setSharedChar notify() called - still in synchronized block.
G consumed by consumer.
Since the output getSharedChar is able to appear before setSharedChar, it appears that the lock is being released immediately or not required to reenter the synchronized getSharedChar() function by the call to notifyAll(). The lock may still be in place, but if you can reenter the function without it, what is the difference? I was able to see similar output substituting notify() for notifyAll(). This was done on Java 1.7.0_15 on a 64 bit Windows 7 system.
wait(): Virtually every object in Java posses a monitor, to enter inside any synchronized block a thread has to first acquire this monitor and then only he can enter this synchronized block. As critical section of the code is executed by a single thread at a time so it has a great impact on overall performance of the application. So in place of holding the resource(monitor) threads can be asked to leave the critical section and wait for some time. To achieve this behaviour Java has provided a wait() api directly in Object class.
So any time a thread encounter a wait() API, it drops the current monitor plus all other monitors it holds and go to waiting state linked the current object. Its important to understand that that went to the waiting state in the context of object for which thread acquired the monitor first.. In a conceptual way i explain, every object holds a container house where all waiting threads are held. There are multiple ways a Thread can come out of this container house of Object. Lets see..
notify(): If the objects container house has multiple threads in waiting state then calling notify() on the this object given chance to one the thread to proceed. But after coming out of the waiting state the thread still has to contest for the object monitor and if it succeeds in getting the monitor it continue its execution otherwise thread will come back to waiting state. So notify() also has to be called from the synchronized block. If the notify() is not called from synchronized context then it thorws IllegalMonitorStateException.
notifyAll(): Calling notifyAll() on the Object makes sure all the threads in Object container house are awakened but once awakened they have to compete with each other or any other thread wants to acquire the object monitor. Which ever thread succeeds continue its executions others have to go back to waiting state and settle in object container house. As notify(), notifyAll() should also be called in synchronized context.
Explanation Taken from http://coder2design.com/thread-communication/
I have to disagree with people who say notifyAll()
releases the lock on the object over which waiting and notifying threads are being synchronized.
An example:
Consumer
class contains a block:
synchronized(sharedObject){
if(sharedObject.isReadyToConsume() == false){
sharedObject.wait();
}else {
sharedObject.doTheThing();
System.out.println("consumer consuming...");
}
}
Scenario: Consumer class gets the lock on the sharedObject object, enters exclusively (it's inside the sync block) and sees that sharedObject has nothing ready yet (nothing to consume :) ) and it calls wait()
method on the sharedObject. That way it releases the lock (stops the execution there!) and waits to be notified to continue when another Thread (Producer maybe) calls sharedObject.notify();
or sharedObject.notifyAll();
. When it gets notified it continues from the wait() line
It's the sharedObject that keeps track of threads that asked it to be notified. When some Thread calls sharedObject.notifyAll() method the sharedObject will notify the waiting threads to wake up... Now, the tricky part is that a thread naturally releases the lock of the object when it reaches the end of its synchronized(sharedObject){} block. THe question is what happens if I call notifyAll() in that block??? notifyAll() wakes up the waiting threads, but the lock is still owned by the Thread that has just call notifyAll()
Look at the Producer snippet:
synchronized(sharedObject){
//We are exlusively working with sharedObject and noone can enter it
[... changing the object ...]
sharedObject.notifyAll(); //notifying the waiting threads to wake up
Thread.sleep(1000); //Telling the current thread to go to sleep. It's holding the LOCK
System.out.println("awake...");
}
If notifyAll() would release the lock then the "awake..." would get printed out after the Consumer classes already start working with the sharedObject. This is not the case... The output shows that the Consumer is consuming the sharedObject after the Producer exits its sync block...
wait( ) tells the calling thread to give up the monitor and go to sleep until some other thread enters the same monitor and calls notify( ).
notify( ) wakes up a thread that called wait( ) on the same object.
notifyAll( ) wakes up all the threads that called wait( ) on the same object. The highest priority thread will run first.
Lets say bunch of Readers wants to read updated Value of certain Resource, which is going to be updated by Writer. Then how Reader knows that Resource Fields have been updated by Writer.
Therefore to Synchronize such cases between Readers and Writers on common resource, three final methods of Object class have been used.
Wait : Readers wants to read updated value of resource, they register with resource object i.e when update happens on same object and when Writer notify it, readers will try to acquire lock on resource and read the updated resource. - Wait only being invoked when Reader have Lock Object, here in our case it is resource. - Once wait method is called, Reader releases the Lock Object. - Now Only for the same registered Object(resource) Reader will get notification signals. - If Reader invokes wait on Object, which is different from Object Writer used for sending notification, Reader will never gets the notification signal. - Once Reader(s) are notified, now Reader(s) will try to content for the Lock(one of them gets the lock) read the updated value of resource. Similarly other Readers also gets turn to acquire lock and read the updated value. - Once Reader read the updated value, perform there Business Logic and come out from Synchronized Block, Reader will release the lock so that other Readers can acquire it.
Notify: Writer enters the Synchronized Block, after acquiring lock performs his Business Logic, update the resource Object, once resource Object is updated it will notify the waiting threads(Readers) whom are waiting on same Lock. - Notify signal to only one waiting Thread, which is decided by underlying Java Thread Manager - Once Writer signals notify(), then it doesn't means Reader rushed immediately to read the updates values. Firstly writer must release the Lock, which it will do once it comes out of Synchronized block.Once Lock is being released and waiting threads are notified, then [In case of notify()] notified Thread will acquire the Lock[Released by Writer] and then enter the Synchronized Block and completes from where he left[i.e statements after wait()].
Notify-All: In notifyAll, all threads who are registered with resource lock, will get the notifications. - Once notifyAll() is triggered, all threads waiting on same lock will get the signal and are ready in contention to acquire the lock. - Once Writer finishes its Job and release the Lock, Any one Reader will acquire the lock[which Thread, again decided by underlying Java Thread Manager Implementation]. - Once the Reader gets the Lock, it will enter in Synchronized Block, where he left[i.e after wait() method] performs it tasks and on completing the Synchronized Block releases the Lock. - Now Other remaining Threads will try to acquire the Lock, anyone of them will get it, enters synchronized block, completes its task and then release the Lock. - This process will keep on till all Registered Readers complete there Job.
Now we will see the Code for it. Also we will discuss the Code also. :
Basic Overview Of code : It consists of Three classes
Resource.java
public class Resource {
private String mesg;
public void setMesg(String mesg){
this.mesg =mesg;
}
public String getMesg(){
return this.mesg;
}
}
WaitThreadTask.java
public class WaitThreadTask implements Runnable {
private Resource resource;
public WaitThreadTask(Resource resource){
this.resource = resource;
}
@Override
public void run() {
// TODO Auto-generated method stub
synchronized(resource){
System.out.println("Before Reading Updated Value By : " +Thread.currentThread().getName() );
//We need to Take care to get the updated value, so waiting for writer thread to update value.
try {
//Release resource Lock & wait till any notification from Writer.
resource.wait();
System.out.println("Waiting is Over For : "+ Thread.currentThread().getName());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Read Updated Value
System.out.println("Updated Value of Resource Mesg :" + resource.getMesg() + " Read By :" +Thread.currentThread().getName());
}
}
}
WriterThreadTask.java
public class WriterThreadTask implements Runnable{
private Resource resource;
public WriterThreadTask(Resource resource){
this.resource = resource;
}
@Override
public void run() {
// TODO Auto-generated method stub
synchronized(resource){
System.out.println("Before Updating Resource By : " + Thread.currentThread().getName());
//Updating resource Object Message
resource.setMesg("Hi How are You !!!");
resource.notify();
//resource.notifyAll();
//Once Writer Comes Out from Synch Block, Readers will Content to read the values.
System.out.println("Task Done By Writer Thread.");
}
}
}
ThreadDemo.java
public class ThreadDemo {
public static void main(String args[]){
//Create Single Resource Object, which can act as Lock on Writer and Readers.
Resource lock = new Resource();
//Three Readers and One Writer runnable Tasks.
Runnable taskR1 = new WaitThreadTask(lock);
Runnable taskR2 = new WaitThreadTask(lock);
Runnable taskR3 = new WaitThreadTask(lock);
Runnable taskW1 = new WriterThreadTask(lock);
Thread t1 = new Thread(taskR1, "Reader1");
Thread t2 = new Thread(taskR2, "Reader2");
Thread t3 = new Thread(taskR3, "Reader3");
Thread t4 = new Thread(taskW1, "Writer1");
t1.start();
t2.start();
t3.start();
/*try{
Thread.sleep(5000);
} catch(InterruptedException e){
e.printStackTrace();
}*/
t4.start();
}
}
Code Observations :