.wait(), .notify(), and .notifyAll() Tutorial

There are a total of 11 methods in the Object class. Three of those methods are overloaded versions of the .wait() method. Combine those three with the .notify() and the .notifyAll() methods and you have almost half of the Object class methods grouped up. What does this group of methods really do? They represent the primitive core communication methods between threads. I went back and forth on whether or not to create a tutorial on these methods because they are pretty much legacy now. The java.util.concurrent package contains many classes and methods that will handle communication between threads in a much more efficient manner. You need to have a general idea of how a Java monitor lock works so you can understand the necessary overhead that comes with using these methods.

The Monitor Lock (Intrinsic lock)

I am going to use a simple analogy of how a ship (boat) lock works in order to describe the stages of a monitor lock. The Panama Canal allows ships to cross back and forth from the Atlantic ocean to the Pacific ocean. There are numerous locks in the Panama Canal, but for purposes of this tutorial imagine that there is just a single lock. The area between the lock gates is where the water is pumped in or out to make the water level either rise or fall. In my analogy this area inside of the gates represents Java object state. If we allow two or more ships into the lock gates at a time we would create a dangerous situation with dire consequences, so we have to synchronize operations by only allowing one ship to be inside the gates at any given time. The same thing applies to object state when you have multiple threads trying to access instance variables via methods. Any methods that access the object state must have the synchronized keyword applied. Now that we have the rules of the lock established let's get some ships (threads) through the locks.
You are the captain of a giant shipping vessel named "The Thread" and you are approaching the canal on the Atlantic side. As you near the canal you notice that there are other ships already waiting there competing to see who can be the next one into the lock gates. At this point you are attempting to enter the canal, so collectively your ship and all of the other ships are part of any entry group or entry set. Just before to canal gates, the entrance to the canal is only wide enough to fit ships in a single file line. As luck would have it, the other captains have fallen asleep so you are able to maneuver your ship right into that skinny channel and you are now waiting just outside of the lock gate. At this point you have acquired the slot to be the next one in the lock gates; you have acquired the lock.
The lock gate in front of you opens up and you maneuver your ship into the area inside the gates, the gate closes behind you and you now own the lock. At this point you have exclusive access to the lock (object state) and you can sit back while the water is pumped either in or out (synchronized methods that either modify or access state).
Once the water level is properly adjusted (your synchronized method has finished executing statements) the gate door will open in front of your ship. You then maneuver your ship out of the lock gates down a thin channel before you hit the open ocean. When your ship is in this channel and the gate has closed behind you, your ship has been released from the lock (released the monitor lock).
Once you exit that channel you are free to navigate the Pacific Ocean (exited the monitor). That is my general overview of what a monitor lock is, so let's move on.

What do the methods do?

  • .wait() · Causes the current thread to wait (give up the monitor and sleep) until another thread invokes notify() or notifyAll().
  • .notify() · Wakes up a single thread that is waiting on the object's monitor (should be the first thread that called wait()).
  • .notifyAll() · Wakes up all threads that are waiting on this object's monitor.

Spurious Wakeup

Based on the description of the methods above it should be fairly simple to use these methods to coordinate thread communication, but wait there's more! So what is a spurious wakeup? A spurious wakeup is when a thread is in a waiting state and then wakes up from the wait state for no reason at all - other threads did not call notify. Spurious wakeups are allowed to happen at the JVM level, meaning that the JVM is allowed to essentially sort of call a notifyAll() for all currently running objects. That throws a serious monkey wrench into this whole equation. To my knowledge, my code has never actually experienced a spurious wakeup, but if you Google "Java spurious wakeup" you will see that it is in fact a real issue. In order to account for a spurious wakeup we must enclose our call to the .wait() method inside of a loop that checks to see if the condition that we are waiting for has actually occurred. Confusing??? Oh yea!

In this tutorial we are going to take the "Lather, rinse, repeat ..." instructions found on many brands of shampoo seriously. Throughout the video tutorial I will be adding a lot of code that is not in the code below.



Open the command prompt (CMD - see the Getting Started ) and type in the following commands.

C:\Windows\System32>cd \
C:\>md Java
C:\>cd Java
C:\Java>
C:\Java>md WaitNotify
C:\Java>cd WaitNotify
C:\Java\WaitNotify>Notepad WaitNotify.java

Copy and Paste, or type the following code into Notepad and be sure to save the file when you are done.


class WaitNotify {
    public static void main(String args[]) {
        CleanHair clean = new CleanHair(); // single object
        new HairThread("Lather", clean);
        new HairThread("Rinse", clean);
    }
}

class CleanHair {
    String currentState=""; // object state
	
    synchronized void Lather() {
        currentState = "Lather";
        System.out.println(currentState);
        notify();
        try {
            wait();
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        }
    }

    synchronized void Rinse() {
        currentState = "Rinse";
        System.out.println(currentState);
        notify();
        try {
            wait();
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        }	
    }

}

class HairThread extends Thread {
    String name;
    CleanHair cleanRef;
		
    HairThread(String name, CleanHair cleanRef) {
        this.name = name;
        this.cleanRef = cleanRef;
        setName(name);
        start();
    }
	
    @Override
    public void run() {
        if (name.equals("Lather")) {
            cleanRef.Lather(); 
        }
        if (name.equals("Rinse")) {
            cleanRef.Rinse(); 
        }
    }
}


Now switch back to the command prompt (CMD) and type in javac WaitNotify.java and press Enter.
Now type in java WaitNotify and press Enter.


C:\Java\WaitNotify>javac WaitNotify.java
C:\Java\WaitNotify>java WaitNotify
See video for results


Final thoughts

If you find yourself in a state of frustration that is a good thing - you are learning. Sometimes new concepts take a while to for new concepts to sink in, try waiting a day and watching the tutorial again.


Tutorials