Thread CommunicationJ8 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 Overview Top
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 Example Top
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.
package info.java8;
/*
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.
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 Example Top
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:
package info.java8;
/*
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.
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:
- Thread 1 gets the lock and enters
method1()
with theisExecuting
flag set totrue
and prints out a message "Bin". After this we callnotify()
on waiting threads and then release theBingo
object lock by calling thewait()
method:- This thread can regain the lock and call
method1()
again with theisExecuting
flag set tofalse
. - This time
method1()
logic will do anotify()
and exit releasing the lock again - After this either thread then has a chance to run again
- This thread can regain the lock and call
- Thread 2 gets the lock and enters
method2()
with theisExecuting
flag set totrue
and prints out a message "GO". After this we callnotify()
on waiting threads and then release theBingo
object lock by calling thewait()
method:- This thread can regain the lock and call
method2()
again with theisExecuting
flag set tofalse
. - This time
method2()
logic will do anotify()
and exit releasing the lock again - After this either thread then has a chance to run again
- This thread can regain the lock and call
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 Threads Top
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:
package info.java8;
/*
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.
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()
andnotifyAll()
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:
package info.java8;
/*
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.
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.
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:
package info.java8;
...
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.
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 Loop Top
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.
package info.java8;
/*
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.
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.