|
|
It's a good thing that we know how to control exclusive access to areas of our code between multiple threads, but this is not enough. We may also need to control when our threads run and how they can talk to one another. Effectively, the threads can tell each other when to run and pause. This is efficiently performed using the wait and notify methods. If you remember back to Chapter 4, "Multiple Classes," we gave these methods a brief mention. They are instance methods of the object class inherited by all objects. In the most basic terms, a wait method invoked by a thread pauses the execution of that thread until another thread invokes a notify method to wake up the sleeping thread.
When using the wait command, you need to make sure that the current thread has ownership of the object's monitor—the object, that is, on which you are invoking the wait method. For the wait method you also need to try/catch an InterruptedException; we will discuss interrupting threads later on. The following code is a typical illustration of pausing a thread using an object's wait method:
synchronized(myObject) { try { myObject.wait(); // Thread A pauses } catch(InterruptedException e) { } }
To begin with, thread A synchronizes on the myObject object to take ownership of its monitor. We can then call the wait method on myObject. When we do this, the thread releases its ownership of myObject's monitor and pauses the thread until another thread calls the notify or notifyAll method of the myObject object. When another thread calls the notify method, a single waiting thread is awoken and is then a candidate to regain ownership of the object's monitor once it becomes available. When another thread calls notifyAll, all threads currently waiting are woken up and become candidates to take ownership of the object's monitor.
Note | There is no guarantee as to which thread, from a list of contenders waiting for ownership of the object's monitor, will be awoken first, although some threads are more likely to be awoken earlier than others. We will look at thread priorities a little later. |
The following code illustrates how another thread, thread B, could wake up thread A.
// Thread B wakes up thread A synchronized(myObject) { myObject.notify(); }
In this code, we can see that in order to call the notify method of an object, the thread must own the object monitor. When thread A called the wait method, this released thread A's ownership of myObject's monitor. This allows thread B's synchronized block to be entered, as the monitor was freed by thread A invoking wait, provided this was the flow of execution, of course. When myObject.notify is invoked, thread B will keep ownership of myObject's monitor until it leaves the synchronized block that encapsulates it. So be aware—calling notify does not mean that thread A will immediately start up again.
There are two more versions of the wait method that we have not mentioned: wait(long timeout) and wait(long timeout, int nanos). The parameter of type long represents time in milliseconds (a thousand milliseconds equals one second); nanos means nanoseconds (a thousand nanoseconds equals one millisecond).
These methods work in the same way, except they also specify a timeout period for the thread to wait on the object's monitor. The thread will wake if another notifies it, as discussed before, or if the specified timeout period elapses.
Note | The accuracy of timing depends not only on your hardware but also on how your Java Virtual Machine implements getting the time – the resolution of the timer it uses for time-related things like this. We will discuss the importance of timers in Chapter 12. |
That is as far as we are going to delve into using wait and notify for the time being. We will implement some practical examples that involve them later in the book (in Chapter 9 for example). Now that we have covered the difficult elements of threads, we can relax a little and look at some more straightforward stuff where threads are concerned.
|
|