|
|
Threads of Control |
Thegetandputmethods in theCubbyHoleobject both make use of thenotifyAllandwaitmethods to coordinate getting and putting values into theCubbyHole. BothnotifyAllandwaitare members of thejava.lang.Objectclass.
Note: ThenotifyAllandwaitmethods can be invoked only by the thread that holds the lock.
Let's investigate
CubbyHole's use of thenotifyAllmethod by looking at thegetmethod.The
notifyAllMethodThegetmethod callsnotifyAllas the last thing it does (besides return). ThenotifyAllmethod 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
Consumerthread calls thegetmethod, so theConsumerthread holds the monitor for theCubbyHoleduring the execution ofget. At the end of thegetmethod, the call tonotifyAllwakes up theProducerthread that is waiting to get theCubbyHole's monitor. Now, theProducerthread can get theCubbyHolemonitor 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
putmethod works in a similar fashion toget, waking up theConsumerthread that is waiting for theProducerto 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 ofnotifycan be ill-conditioned, that is, it can fail when the conditions are changed just a little. Solutions based onnotifyAlltend to be more robust. A programmer who is not going to analyze a multi-threaded program sufficiently carefully is better off usingnotifyAll.The
waitMethodTheobject waitmethod causes the current thread to wait (possibly forever) until another thread notifies it of a condition change. You usewaitin conjunction withnotifyornotifyAllto coordinate the activities of multiple threads using the same resources.The
getmethod contains awhilestatement that loops untilavailablebecomestrue. Ifavailableis false--theProducerhas not yet produced a new number and theConsumershould wait--thegetmethod callswait.The
whileloop contains the call towait. Thewaitmethod waits indefinitely for a notification from theProducerthread. When theputmethod callsnotifyAll, aConsumerwakes up from the wait state and continues within the while loop. Presumably, theProducerhas generated a new number and thegetmethod drops out of thewhileloop and proceeds. If theProducerhas not generated a number,getgoes back to the beginning of the loop and continues to wait until theProducergenerates 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
putmethod works in a similar fashion, waiting for theConsumerthread to consume the current value before allowing theProducerto produce a new one.Besides the version used in the producer/consumer example, which waits indefinitely for notification, the
Objectclass contains two other versions of thewaitmethod:
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
timeoutmilliseconds plus nanos nanoseconds have elapsed.
Note: Besides using these timedwaitmethods 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 upwaitwith 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'sputandgetmethods. At the beginning of thegetmethod, if the value in theCubbyHoleis not available (theProducerhas not generated a new number since the last time theConsumerconsumed it), then theConsumerwaits for theProducerto put a new value into theCubbyHole. So, the question arises--how can theProducerput a new value into theCubbyHoleif theConsumerholds the monitor? (TheConsumerholds theCubbyHole's monitor because it's within the synchronized methodget.)Similarly, at the beginning of the
putmethod, if the value in theCubbyHolehas not yet been consumed, then theProducerwaits for theConsumerto consume the value in theCubbyHole. And again the question arises--how can theConsumerconsume the value in theCubbyHole, if theProducerholds the monitor? (TheProducerholds 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
waitmethod, which happens at the beginning of both theputandgetmethods, the monitor is released atomically, and when the thread exits thewaitmethod, 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 theCubbyHoleor produce a new value for theCubbyHole.
|
|
Threads of Control |