Threads of Control |
Theget
andput
methods in theCubbyHole
object both make use of thenotifyAll
andwait
methods to coordinate getting and putting values into theCubbyHole
. BothnotifyAll
andwait
are members of thejava.lang.Object
class.
Note: ThenotifyAll
andwait
methods can be invoked only by the thread that holds the lock.
Let's investigate
CubbyHole
's use of thenotifyAll
method by looking at theget
method.The
notifyAll
MethodTheget
method callsnotifyAll
as the last thing it does (besides return). ThenotifyAll
method notifies all the threads waiting on the monitor held by the current thread and wakes them up. Typically, one of the waiting threads will grab the monitor and proceed.In the case of the producer/consumer example, the
Consumer
thread calls theget
method, so theConsumer
thread holds the monitor for theCubbyHole
during the execution ofget
. At the end of theget
method, the call tonotifyAll
wakes up theProducer
thread that is waiting to get theCubbyHole
's monitor. Now, theProducer
thread can get theCubbyHole
monitor and proceed.If multiple threads are waiting for a monitor, the Java runtime system chooses one of the waiting threads to run, making no commitments or guarantees about which thread will be chosen.public synchronized int get() { while (available == false) { try { wait(); } catch (InterruptedException e) { } } available = false; notifyAll(); // notifies Producer return contents; }The
put
method works in a similar fashion toget
, waking up theConsumer
thread that is waiting for theProducer
to release the monitor.The Object class has another method--
notify
--that arbitrarily wakes up one of the threads waiting on the monitor. In this situation, each of the remaining waiting threads continues to wait until the monitor is relinquished and it is chosen by the runtime system. The use ofnotify
can be ill-conditioned, that is, it can fail when the conditions are changed just a little. Solutions based onnotifyAll
tend to be more robust. A programmer who is not going to analyze a multi-threaded program sufficiently carefully is better off usingnotifyAll
.The
wait
MethodTheobject wait
method causes the current thread to wait (possibly forever) until another thread notifies it of a condition change. You usewait
in conjunction withnotify
ornotifyAll
to coordinate the activities of multiple threads using the same resources.The
get
method contains awhile
statement that loops untilavailable
becomestrue
. Ifavailable
is false--theProducer
has not yet produced a new number and theConsumer
should wait--theget
method callswait
.The
while
loop contains the call towait
. Thewait
method waits indefinitely for a notification from theProducer
thread. When theput
method callsnotifyAll
, aConsumer
wakes up from the wait state and continues within the while loop. Presumably, theProducer
has generated a new number and theget
method drops out of thewhile
loop and proceeds. If theProducer
has not generated a number,get
goes back to the beginning of the loop and continues to wait until theProducer
generates a new number and callsnotifyAll
.public synchronized int get() { while (available == false) { try { wait(); // waits for notifyAll() call from Producer } catch (InterruptedException e) { } } available = false; notifyAll(); return contents; }The
put
method works in a similar fashion, waiting for theConsumer
thread to consume the current value before allowing theProducer
to produce a new one.Besides the version used in the producer/consumer example, which waits indefinitely for notification, the
Object
class contains two other versions of thewait
method:
wait(long timeout)
- Waits for notification or until the timeout period has elapsed. timeout is measured in milliseconds.
wait(long timeout, int nanos)
- Waits for notification or until
timeout
milliseconds plus nanos nanoseconds have elapsed.
Note: Besides using these timedwait
methods to synchronize threads, you can also use them in place ofsleep
. Both methods delay for the requested amount of time, but you can easily wake upwait
with anotify
. The thread can then see immediately that it should go away. This doesn't matter too much for threads that don't sleep for long, but it could be important for threads that sleep for minutes at a time.
notify
and wait
MethodsYou might have noticed a potential problem inCubbyHole
'sput
andget
methods. At the beginning of theget
method, if the value in theCubbyHole
is not available (theProducer
has not generated a new number since the last time theConsumer
consumed it), then theConsumer
waits for theProducer
to put a new value into theCubbyHole
. So, the question arises--how can theProducer
put a new value into theCubbyHole
if theConsumer
holds the monitor? (TheConsumer
holds theCubbyHole
's monitor because it's within the synchronized methodget
.)Similarly, at the beginning of the
put
method, if the value in theCubbyHole
has not yet been consumed, then theProducer
waits for theConsumer
to consume the value in theCubbyHole
. And again the question arises--how can theConsumer
consume the value in theCubbyHole
, if theProducer
holds the monitor? (TheProducer
holds theCubbyHole
's monitor because it's within the synchronized methodput
.)Well, the designers of the Java language thought of this too. When the thread enters the
wait
method, which happens at the beginning of both theput
andget
methods, the monitor is released atomically, and when the thread exits thewait
method, the monitor is acquired again. This gives the waiting object the opportunity to acquire the monitor and, depending on who's waiting, consume the value in theCubbyHole
or produce a new value for theCubbyHole
.
Threads of Control |