Search code examples
javamultithreadingconcurrencyjava-threads

Behavior of wait() and notifyAll() in Java?


Please note that this is not the actual scenario. I created a sample scenario based on my actual implementation, to make it easy to review. I am also already getting the expected output too. However, I need to clarify some concepts regarding the wait() and notifyAll() methods in Java. (In here both these threads will starts there run method at once in the main thread.) So according to my knowledge, since thread B is sleeping, because you can see at the initial stage reamingCount is 400.

So thread B will calls its MUTEX.wait() and continue its sleep until some other thread invokes a notify() or notifyAll(), then after the remainingCount decrements to 0, thread A will call MUTEX.notifyAll(); to awake the thread B and MUTEX.wait() to release its already granted lock, and go to sleep until thread B notifies it.

When I call MUTEX.notifyAll() through thread A, won't thread B wake up and continue its task before thread A calls MUTEX.wait()?

I mean, you can see when thread A calls the MUTEX.notifyAll(), thread B will awake and check again if the condition in the while loop is true or false. So, since the remainingCount is equal to 0, thread B will exit the while loop and continue its task before thread A calls wait(). Won't this scenario break the principle of wait()? According to my knowledge thread B can only continue its execution when thread A calls wait().

public class A implements Runnable{

    public static volatile remainingCount =400;
    private final Object MUTEX;//Both class A and B holds the same object mutex

    private void methodA(){

         synchronized(MUTEX){
             
              while(remainingCount == 0){

                     MUTEX.notifyAll();
                     MUTEX.wait();
              }

              //Perform it's usual task.In here remaining count will decrement during the process.
              

        }
      @Override
      public void run() {

        while(true){
          methodA();
        }
     }
   }
}
public class B implements Runnable{

         private final Object MUTEX;//Both class A and B holds the same object mutex

         private void methodB(){
              synchronized(MUTEX){

                   while (A.remainingCount != 0) {
                        try {
                    
                               MUTEX.wait();
                     
                        } catch (InterruptedException ex) {
                               Logger.getLogger(InkServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
                        }
                   }
                      //incrementing the A.remainingCount 

                      MUTEX.notifyAll();
 
            }
            
           @Override
           public void run() {

                  while(true){
                     methodB();

                   }
             }
}


Solution

  • When a thread holding a lock calls wait() on the locked object, the thread is added to the object's wait set and the lock is released.

    When a thread holding a lock calls notify(), and the wait set is not empty, a thread in the wait set is selected and removed. Likewise, calling notifyAll() removes all threads from the wait set.

    Note: threads can also be removed from the wait set by a call to thread.interrupt().

    When a thread is removed from the wait set and begins to run, the first step is to reacquire the lock. This happens before the return from wait().

    This will not happen until the thread that called notify() or notifyAll() releases the lock by either calling wait() or exiting the synchronized block.

    So, while your thread B has been enabled to run, it won't actually return from wait() until thread A releases the lock by calling MUTEX.wait(). Likewise, thread A is enabled to run when B calls MUTEX.notifyAll(), but doesn't return from wait() until thread B exits the synchronized(MUTEX) block.