Thread CommunicationS2C Home « Thread Communication

So far in this section we have seen how we can stop threads from running using the sleep() method of the Thread class to allow other threads some processing time. We have also used the synchronized keyword to lock an object within a method or a block of code. We looked at how we can enter a deadlock scenario when two or more objects go into a deadly embrace waiting for the lock of the other object. In the last lesson we looked at thread priorities and how we can assign priorities to different threads to bring some order to thread selection in unison with the use of the yield() method.

In our final lesson of the section we explore how threads can communicate with each other when they are within a synchronized method or code block. The Object class comes with three methods that are for use within synchronized sections of code and as they belong to the Object class are available through inheritance to all objects we create. The methods in question are wait(), notify() and notifyAll() and allow threads to communicate to other threads that they are entering a blocked state or leaving a blocked state so they can cease or resume execution respectively.

Object Methods OverviewTop

The table below shows the declarations of the wait(), notify() and notifyAll() methods of the Object class:

Method Declaration Description
public final void notify()Wakes up a single thread that is waiting on the invoking object's lock.
public final void notifyAll() Wakes up all threads that are waiting on the invoking object's lock.
public final void wait()Current thread is put into a wait state until another thread invokes the notify()or notifyAll() method for this object.
public final void wait(long timeout)Current thread is put into a wait state until either another thread invokes the notify() or notifyAll() method for this object, or a specified amount of time has elapsed.
public final void wait(long timeout, int nanos)Current thread is put into a wait state until another thread invokes the notify() or notifyAll() method for this object, or a specified amount of time has elapsed, or a certain amount of real time has elapsed.

We will go through the methods mentioned in much greater detail as we go through this lesson. Use the links in the table above to go to the code example where the method is first used.

A Synchronized ExampleTop

In the example code below we are creating two threads for the same object and passing them through some synchronized methods. We looked into synchronized methods and blocks of code in the Synchronization lesson, so nothing new here, just usage before we do some thread communication so we can see the difference.


/*
  A sync example
*/
public class TestBingo {
    static class Bingo {
        public synchronized void method1(boolean isExecuting) {
            if (isExecuting) { 
                System.out.println("Bin");
            }
        }

        public synchronized void method2(boolean isExecuting) {
            if (isExecuting) {
                System.out.print("GO");
            }
        }
    }

    public static void main(String[] args) {
        final Bingo bingo = new Bingo();
        // Create runnable thread using anonymous inner class
        new Thread(new Runnable() {
            public void run() { 
                for (int i=0; i<10; i++) {
                    bingo.method1(true); 
                    bingo.method1(false); 
                }
            }
        }).start();
        // Create runnable thread using anonymous inner class
        new Thread(new Runnable() {
            public void run() { 
                for (int i=0; i<10; i++) {
                    bingo.method2(true); 
                    bingo.method2(false); 
                }
            }
        }).start();
    }
}

Save, compile and run the TestBingo class in directory   c:\_Concurrency in the usual way.

test bingo synch

The screenshot shows the results of three runs of the TestBingo class and as you can see the results, as we would expect, are fairly random depending on which thread has control of the object lock. The running of the methods is purely down to the scheduler moving the threads in and out of a runnable state.

A Communicating ExampleTop

As mentioned at the start of the lesson the Object class provides us with methods we can use within synchronized sections of code.

The notify() method wakes up a single thread that is waiting on the invoking object's lock and the notifyAll() method wakes up all threads that are waiting on the invoking object's lock. Both the notify() and notifyAll() methods don't give up an object's lock, although generally the thread in question exits the synchronized code block shortly after using these methods, which then releases the lock.

The overloaded wait() method unlike the notify() and notifyAll() methods and the join(), sleep() and yield() methods we have seen earlier in the section, will release an object's lock so that any runnable threads have a chance to use the object in question. So by using the notify() / notifyAll() and wait() methods we can orchestrate our objects to get threads running in a more orderly manner. To see these methods in use lets modify the TestBingo class we used above:


/*
  A sync with comms example
*/
public class TestBingo {
    static class Bingo {
        public synchronized void method1(boolean isExecuting) {
            if (isExecuting) {
                System.out.print("Bin");
                notify(); // Let method2() run
                try {
                    wait(); // Wait for method2() to complete
                } catch (InterruptedException ex) {
                    System.out.println(ex);
                } 
            } else {
                notify();
            }
        }
        public synchronized void method2(boolean isExecuting) {
            if (isExecuting) {
                System.out.println("GO");
                notify(); // Let method1() run
                try {
                    wait(); // Wait for method1() to complete
                } catch (InterruptedException ex) {
                    System.out.println(ex);
                }
            } else {
                notify();
            } 
        }
    }

    public static void main(String[] args) {
        final Bingo bingo = new Bingo();
        // Create runnable thread using anonymous inner class
        new Thread(new Runnable() {
            public void run() { 
                for (int i=0; i<10; i++) {
                    bingo.method1(true); 
                    bingo.method1(false); 
                }
            }
        }).start();
        // Create runnable thread using anonymous inner class
        new Thread(new Runnable() {
            public void run() { 
                for (int i=0; i<10; i++) {
                    bingo.method2(true); 
                    bingo.method2(false); 
                }
            }
        }).start();
    }
}

Save, compile and run the revamped TestBingo class in directory   c:\_Concurrency in the usual way.

test bingo comm

The screenshot shows the results of three runs of the reworked TestBingo class and this time there is a definite pattern where the thread that invokes method1() runs and then the thread that invokes method2() runs. One of two scenarios can happen:

  1. Thread 1 gets the lock and enters method1() with the isExecuting flag set to true and prints out a message "Bin". After this we call notify() on waiting threads and then release the Bingo object lock by calling the wait() method:
    • This thread can regain the lock and call method1() again with the isExecuting flag set to false.
    • This time method1() logic will do a notify() and exit releasing the lock again
    • After this either thread then has a chance to run again
  2. Thread 2 gets the lock and enters method2() with the isExecuting flag set to true and prints out a message "GO". After this we call notify() on waiting threads and then release the Bingo object lock by calling the wait() method:
    • This thread can regain the lock and call method2() again with the isExecuting flag set to false.
    • This time method2() logic will do a notify() and exit releasing the lock again
    • After this either thread then has a chance to run again

The above two scenarios can get repeated over and over again and the most likely output is that we get "BinGO" printed out as in the screenshot. This is because it's a more likely event than one thread getting the lock three times in a row.

If thread 2 gets first shot then in all likelihood the output would be mainly "GOBin" because it's a more likely event than one thread getting the lock three times in a row.

Of course these methods and output are simplified for learning and you can ensure thread order with more rigorous coding than that shown. We will go into this later in this lesson when we look at putting wait() in a loop.

Releasing All ThreadsTop

In the previous part of this lesson we saw usage of the wait() and notify() methods and how we can get them to interact on two threads. But what if we have several threads that are all put into a waiting state and we then use the notify() method? Only one thread will be put back into a runnable state and which is decided by the JVM and we as programmers have no control which thread that will be. Lets take a look at a scenario where we have some threads in a waiting state and we use the notify() method to release one of them:


/*
  Multiple waiting threads and one notified
  wait() and notify() used on Thread
*/
public class TestWaitNotify {
    static class ShowTotalizer extends Thread {
        Totalizer t;
        
        public ShowTotalizer(Totalizer t) {
            this.t = t;
        }
        public void run() { 
            // Lock on Totalizer object 
            synchronized (t) {
                try {
                    System.out.println(Thread.currentThread().getName() + " waiting for totalizer!!");
                    t.wait();
                } catch (InterruptedException ex) {
                    System.out.println(ex);
                } 
                System.out.println(Thread.currentThread().getName() + " Totalizer count " + t.count);
            }
        }
    }
    static class Totalizer extends Thread {
        int count;
        
        public void run() { 
            // Lock on Totalizer object 
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + " is totalizing!!");
                for (int i=0; i<10; i++) {
                    count += i; 
                }
                // Notify a single thread 
                notify();
            }
        }
    }
    public static void main(String[] args) {
        Totalizer t = new Totalizer();
        new ShowTotalizer(t).start();
        new ShowTotalizer(t).start();
        new ShowTotalizer(t).start();
        new ShowTotalizer(t).start();
        t.start();
    }
}

Save, compile and run the TestWaitNotify class in directory   c:\_Concurrency in the usual way.

test wait notify thread

The screenshot shows the results of running the TestWaitNotify class. The results are not as we expected as we can clearly see from the screenshot that all the waiting threads have been notified somehow? This brings us to some points which are vaguely documented by the Java API documentation:

  • A thread will do a notifyAll() when it ends, which is what happened with our example.
  • Java uses wait() and notifyAll() on threads, to internally implement join(), so interfering with this behaviour can lead to strange results.
  • A thread can do a spurious wakeup, which means it can wake without being interrupted, notified, or timing out and this will be discussed in putting wait() in a loop.

What we need to do is make the Totalizer object, we are waiting and notifying on, a non-thread object and for simplicity we will just make the Totalizer object implement Runnable instead. Below is the reworked TestWaitNotify class:


/*
  Multiple waiting threads and one notified
  wait() and notify() used on Runnable
*/
public class TestWaitNotify {
    static class ShowTotalizer extends Thread {
        Totalizer t;
        
        public ShowTotalizer(Totalizer t) {
            this.t = t;
        }
        public void run() { 
            // Lock on Totalizer object 
            synchronized (t) {
                try {
                    System.out.println(Thread.currentThread().getName() + " waiting for totalizer!!");
                    t.wait();
                } catch (InterruptedException ex) {
                    System.out.println(ex);
                } 
                System.out.println(Thread.currentThread().getName() + " Totalizer count " + t.count);
            }
        }
    }
    static class Totalizer implements Runnable {
        int count;
        
        public void run() { 
            // Lock on Totalizer object 
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + " is totalizing!!");
                for (int i=0; i<10; i++) {
                    count += i; 
                }
                // Notify a single thread 
                notify();
            }
        }
    }
    public static void main(String[] args) {
        Totalizer t = new Totalizer();
        new ShowTotalizer(t).start();
        new ShowTotalizer(t).start();
        new ShowTotalizer(t).start();
        new ShowTotalizer(t).start();
        // We now need to create a new thread to run our Runnable 
        new Thread(t).start();
    }
}

Save, compile and run the reworked TestWaitNotify class in directory   c:\_Concurrency in the usual way.

test wait notify

The screenshot shows the results of running the reworked TestWaitNotify class and this time only one of our threads gets notified as expected. If you run this code you will need to CTRL-C to end it as the other three threads are sitting there waiting to be notified.

Ok all we need to do to see the notifyAll in action is to change the call to notify to a call to notifyAll.

Once you have made the change save, compile and run the revamped TestWaitNotify class in directory   c:\_Concurrency in the usual way.

test wait notify

The screenshot shows the results of running the revamped TestWaitNotify class and this time when I run this on my machine, once again only one of our threads get notified. If you run this code you may need to CTRL-C to end it as some of the other threads may still be sitting there waiting to be notified.

Ok, so what went wrong this time? Well looking at the screeshot closely we can see that Thread-4 which relates to the Totalizer object we create actually runs after Thread-0 and then does the notifyAll(). Then Thread-3, Thread-2 and Thread-1 run and go into a waiting state and will just sit there waiting as nothing is going to notify them now.

What we need to do is ensure that the ShowTotalizer threads all run before the thread which relates to the Totalizer object. To do this we will put the thread relating to the Totalizer object to sleep before we synch on it. So within the run() method of the Totalizer class we will sleep the thread method for a second to achieve this as follows:


     ...
     static class Totalizer implements Runnable {
        int count;
        
        public void run() { 
            // Sleep the thread to ensure the other threads are all waiting
            try {  
                Thread.sleep(1000);  
            } catch(Exception ex) {
                System.out.println(ex);
            }  
            // Lock on Totalizer object 
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + " is totalizing!!");
                for (int i=0; i<10; i++) {
                    count += i; 
                }
                // Notify all threads 
                notifyAll();
            }
        }
    }
    ...

Once you have made the changes save, compile and run our final version of the TestWaitNotify class in directory   c:\_Concurrency in the usual way.

test wait notify

The screenshot shows the results of running our final version TestWaitNotify class and this time all the threads get notified and the code finishes correctly.

Putting wait() In A LoopTop

We saw in the last part of this lesson that using the wait(), notify() and notifyAll() methods in unison is not nearly as simple as it first seems. After many attemps we ended up with a rather inelegant solution that slept the notifying thread for a period of time to ensure the waiting threads went into a wait state before notification. Our problem was we had no guarantee of when our threads got put into a waiting state and therefore no guarantee that using the notify() and notifyAll() methods would wake a single thread, all the threads or indeed any of our threads. To quote the Java API Documentation from the Object class for the wait() method:

  • A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one:

     synchronized (obj) {
         while (<condition does not hold>)
             obj.wait(timeout);
         ... // Perform action appropriate to condition
     }

By coding to a conditional loop we can ensure that we only ever enter a wait state when it is necessary. By doing this we avoid spurious wakeups and threads being sent to a wait state after any nofications have already been sent.


/*
  Multiple waiting threads and one notified
  wait() loop and notify() used on Runnable
*/
public class TestWaitNotifyLoop {
    static class ShowTotalizer extends Thread {
        Totalizer t;
        
        public ShowTotalizer(Totalizer t) {
            this.t = t;
        }
        public void run() { 
            // Lock on Totalizer object 
            synchronized (t) {
                while (t.stillTotalling) {
                    try {
                        System.out.println(Thread.currentThread().getName() + " waiting for totalizer!!");
                        t.wait();
                    } catch (InterruptedException ex) {
                        System.out.println(ex);
                    } 
                }
                // When we get here we know the Totalizer thread has finished 
                System.out.println(Thread.currentThread().getName() + " Totalizer count " + t.count);
            }
        }
    }
    static class Totalizer implements Runnable {
        boolean stillTotalling = true;
        int count;
        
        public void run() { 
            // Lock on Totalizer object 
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + " is totalizing!!");
                for (int i=0; i<10; i++) {
                    count += i; 
                }
                // Totalling done so set boolean to flase, so no more threads put into a wait state 
                stillTotalling = false;
                // Notify all thread 
                notifyAll();
            }
        }
    }
    public static void main(String[] args) {
        Totalizer t = new Totalizer();
        new ShowTotalizer(t).start();
        new ShowTotalizer(t).start();
        new ShowTotalizer(t).start();
        new ShowTotalizer(t).start();
        // We now need to create a new thread to run our Runnable 
        new Thread(t).start();
    }
}

Save, compile and run the TestWaitNotifyLoop class in directory   c:\_Concurrency in the usual way.

test wait notify

Now we have put our wait() method inside a while loop only ShowTotalizer threads that run before the Totalizer thread finishes are put into a wait state. As soon as the Totalizer thread finishes totalling we set the boolean stillTotalling to false and do a notifyAll() on all threads currently in a wait state. Any remaining ShowTotalizer threads will never be put into a wait state and so now there is no need to worry about spuriously woken or hanging threads.

Lesson 5 Complete

In our final lesson of the section we explore how threads can communicate with each other when they are within a synchronized method or code block.

What's Next?

We start a new section looking at collections and generics.

go to home page Homepage go to top of page Top