Java Reference
In-Depth Information
Lock and Condition vs. the synchronized Keyword
In some applications, using Lock and Condition objects may be preferable to using the syn-
chronized keyword. Lock s allow you to interrupt waiting threads or to specify a timeout for
waiting to acquire a lock, which is not possible using the synchronized keyword. Also, a
Lock is not constrained to be acquired and released in the same block of code, which is the
case with the synchronized keyword. Condition objects allow you to specify multiple con-
ditions on which threads may wait . Thus, it's possible to indicate to waiting threads that a
specific condition object is now true by calling signal or signallAll on that Condition ob-
ject. With synchronized , there's no way to explicitly state the condition on which threads
are waiting, and thus there's no way to notify threads waiting on one condition that they may
proceed without also signaling threads waiting on any other conditions. There are other pos-
sible advantages to using Lock and Condition objects, but generally it's best to use the syn-
chronized keyword unless your application requires advanced synchronization capabilities.
Software Engineering Observation 23.7
Think of Lock and Condition as an advanced version of synchronized . Lock and
Condition support timed waits, interruptible waits and multiple Condition queues per
Lock —if you do not need one of these features, you do not need Lock and Condition .
Error-Prevention Tip 23.6
Using interfaces Lock and Condition is error prone— unlock is not guaranteed to be
called, whereas the monitor in a synchronized statement will always be released when
the statement completes execution. Of course, you can guarantee that unlock will be
called if it's placed in a finally block, as we do in Fig. 23.20.
Using Lock s and Condition s to Implement Synchronization
We now implement the producer/consumer relationship using Lock and Condition ob-
jects to coordinate access to a shared single-element buffer (Figs. 23.20 and 23.21). In this
case, each produced value is correctly consumed exactly once. Again, we reuse interface
Buffer and classes Producer and Consumer from the example in Section 23.5, except that
line 28 is removed from class Producer and class Consumer .
Class SynchronizedBuffer
Class SynchronizedBuffer (Fig. 23.20) contains five fields. Line 11 creates a new object
of type ReentrantLock and assigns its reference to Lock variable accessLock . The Reen-
trantLock is created without the fairness policy because at any time only a single Producer
or Consumer will be waiting to acquire the Lock in this example. Lines 14-15 create two
Condition s using Lock method newCondition . Condition canWrite contains a queue for
a Producer thread waiting while the buffer is full (i.e., there's data in the buffer that the
Consumer has not read yet). If the buffer is full , the Producer calls method await on this
Condition . When the Consumer reads data from a full buffer, it calls method signal on
this Condition . Condition canRead contains a queue for a Consumer thread waiting while
the buffer is empty (i.e., there's no data in the buffer for the Consumer to read). If the buffer
is empty , the Consumer calls method await on this Condition . When the Producer writes
to the empty buffer, it calls method signal on this Condition . The int variable buffer
(line 17) holds the shared mutable data. The boolean variable occupied (line 18) keeps
track of whether the buffer currently holds data (that the Consumer should read).
 
Search WWH ::




Custom Search