THE Java™ Programming Language, Fourth Edition

(Jeff_L) #1
Everything is executed within synchronized code. If it were not, the state of the object would not be
stable. For example, if the method were not declared synchronized, then after the while
statement, there would be no guarantee that the condition remained TRue: Another thread might have
changed the situation that the condition tests.


One of the important aspects of the definition of wait is that when it pauses the thread, it atomically
releases the lock on the object. Saying that the thread suspension and lock release are atomic means
that they happen together, indivisibly. Otherwise, there would be a race hazard: A notification could
happen after the lock is released but before the thread is suspended. The notification would have no
effect on the thread, effectively getting lost. When a thread is restarted after being notified, the lock is
atomically reacquired.


The condition test should always be in a loop. Never assume that being awakened means that the
condition has been satisfiedit may have changed again since being satisfied. In other words, don't
change the while to an if.


On the other side, the notification methods are invoked by synchronized code that changes one or more
conditions on which some other thread may be waiting. Notification code typically looks something like this:


synchronized void changeCondition() {
... change some value used in a condition test ...
notifyAll(); // or notify()
}


Using notifyAll wakes up all waiting threads, whereas notify picks only one thread to wake up.


Multiple threads may be waiting on the same object, possibly for different conditions. If they are waiting for
different conditions, you should always use notifyAll to wake up all waiting threads instead of using
notify. Otherwise, you may wake up a thread that is waiting for a different condition from the one you
satisfied. That thread will discover that its condition has not been satisfied and go back to waiting, while some
thread waiting on the condition you did satisfy will never get awakened. Using notify is an optimization
that can be applied only when:



  • All threads are waiting for the same condition

  • At most one thread can benefit from the condition being met

  • This is contractually true for all possible subclasses


Otherwise you must use notifyAll. If a subclass violates either of the first two conditions, code in the
superclass that uses notify may well be broken. To that end it is important that waiting and notification
strategies, which include identifying the reference used (this or some other field), are documented for use
by extended classes.


The following example implements the PrintQueue class that we used with the PrintServer on page



  1. We reuse the SingleLinkQueue class that we defined in Chapter 11 to actually store the print jobs,
    and add the necessary synchronization:


class PrintQueue {
private SingleLinkQueue queue =
new SingleLinkQueue();


public synchronized void add(PrintJob j) {
queue.add(j);
notifyAll(); // Tell waiters: print job added
}


public synchronized PrintJob remove()

Free download pdf