Friday, January 3, 2020

Does notify/notifyall release the lock being held

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() - releases the lock and continues on the next line when it gets notified
  • notify(), notifyAll() - don't release the lock. They simply make waiting threads runnable again (not idle). They will have the right to enter when the current thread reaches the end of its sync block and the Thread scheduleder tells them that the lock has been released. The fight for the lock begins again

No comments:

Post a Comment