CubbyHole
抯
put
and
get
methods helps the
Producer
and the
Consumer
coordinate their activities. The
CubbyHole
stores its value in a private member variable called
contents
.
CubbyHole
has another private member variable,
available
, that is a boolean. The
available
variable is
true
when the value has been put but not yet gotten and is
false
when the value has been gotten but not yet put. Here抯 one possible implementation for the
put
and
get
methods:
As implemented, these two methods won抰 work. Look at thepublic synchronized int get() { //won抰 work! if (available == true) { available = false; return contents; } } public synchronized int put(int value) { //won抰 work! if (available == false) { available = true; contents = value; } }
get
method. What happens if the
Producer
hasn抰 put anything in the
CubbyHole
and
available
isn抰
true
? The
get
method does nothing. Similarly, if the
Producer
calls put before the
Consumer
got the value,
put
doesn抰 do anything.
You really want the Consumer
to wait until the Producer
puts something in the CubbyHole
and the Producer
to notify the Consumer
when it抯 done so. Similarly, the Producer
should wait until the Consumer
takes a value (and notifies the Producer
of its activities) before replacing it with a new value. The two threads must coordinate more fully and can use Object
抯 wait
and notifyAll
methods to do so.
Here are the new get
and put
implementations that wait on and notify each other of their activities:
The code in thepublic synchronized int get() { while (available == false) { try { //wait forProducer
to put value wait(); } catch (InterruptedException e) { } } available = false; //notifyProducer
that value has been retrieved notifyAll(); return contents; } public synchronized void put(int value) { while (available == true) { try { //wait forConsumer
to get value wait(); } catch (InterruptedException e) { } } contents = value; available = true; //notifyConsumer
that value has been set notifyAll(); }
get
method loops until the Producer
has produced a new value. Each time through the loop, get
calls the wait
method. The wait
method relinquishes the lock held by the Consumer
on the CubbyHole
(thereby allowing the Producer
to get the lock and update the CubbyHole
) and then waits for notification from the Producer
. When Producer
puts something in the CubbyHole
, it notifies Consumer
by calling notifyAll
. The Consumer
then comes out of the wait state and the get
method returns the value in the CubbyHole
.
The put
method works in a similar fashion. It waits for the Consumer
thread to consume the current value before allowing the Producer
to produce a new one.
The notifyAll
method wakes up all threads waiting on the object in question (in this case, the CubbyHole
). The awakened threads compete for the lock. One thread gets it, and the others go back to waiting. The Object
class also defines the notify
method, which arbitrarily wakes up one of the threads waiting on this object.
There are the three versions of the wait
method contained in the Object
class:
-
wait()
- Waits indefinitely for notification. (This method was used in the producer-consumer example.) 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 plusnanos
nanoseconds have elapsed.
Note: Besides using these timedwait
methods to synchronize threads, you also can use them in place ofsleep
. Bothwait
andsleep
delay for the requested amount of time. You can easily wake upwait
with anotify
but a sleeping thread cannot be awakened prematurely. 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.