Java Reference
In-Depth Information
keepWorking method on the worker thread, but the invocation blocks because keepWorking is
a synchronized method and the main thread is currently executing a synchronized method on
the worker thread (the quit method).
600 ms: The worker thread again checks whether it's quitting time. Because the quittingTime
field is volatile, the worker thread is guaranteed to see the new value of TRue , so it prints Beer
is good and completes execution. This causes the main thread's join invocation to return,
and the main thread completes execution. The timer thread is a daemon, so it too completes
execution, and the program terminates.
Therefore, we expect the program to run for a bit under a second, print Beer is good , and
terminate normally. If you tried running the program, though, you found that it prints nothing; it just
hangs. What is wrong with our analysis?
There is no guarantee that the events will interleave as indicated in the time line. Neither the Timer
class nor the Thread.sleep method offers real-time guarantees. That said, it's very likely that these
events will interleave as indicated by the time line, as the time granularity is so coarse. A hundred
milliseconds is an eternity to a computer. Moreover, the program hangs repeatably; it looks as if
there is something else at work here, and indeed there is.
Our analysis contains a fundamental flaw. At 500 ms, when the timer task, representing the evil
boss executes, the time line indicates that its keepWorking invocation will block because
keepWorking is a synchronized method and the main thread is currently executing the synchronized
quit method on the same object (waiting in THRead.join ). It is true that keepWorking is a
synchronized method and that the main thread is currently executing the synchronized quit method
on the same object. Even so, the timer thread is able to obtain the lock on this object and execute
the keepWorking method. How can this be?
The answer concerns the implementation of Thread.join . It can't be found in the documentation
for this method, at least in releases up to and including release 5.0. Internally, Thread.join calls
Object.wait on the Thread instance representing the thread being joined. This releases the
lock for the duration of the wait. In the case of our program, this allows the timer thread,
representing the evil boss, to waltz in and set quittingTime back to false , even though the main
thread is currently executing the synchronized quit method. As a consequence, the worker thread
never sees that it's quitting time and keeps running forever. The main thread, representing the good
boss, never returns from the join method.
The fundamental cause of the misbehavior of the program is that the author of the WorkerThread
class used the instance lock to ensure mutual exclusion between the quit and keepWorking
methods, but this use conflicts with the internal use of this lock by the superclass ( THRead ). The
lesson is: Don't assume anything about what a library class will or won't do with locks on its
instances or on the class, beyond what is guaranteed by the class's specification. Any call to a
library could result in a call to wait , notify , notifyAll , or a synchronized method. All these things
can have an effect on application-level code.
If you need full control over a lock, make sure that no one else can gain access to it. If your
class extends a library class that might use its locks or if untrusted parties might gain access to
instances of your class, don't use the locks that are automatically associated with the class or its
instances. Instead, create a separate lock object in a private field. Prior to release 5.0, the correct
type to use for this lock object was simply Object or a trivial subclass. As of release 5.0,
 
 
Search WWH ::




Custom Search