Java Reference
In-Depth Information
Chapter 14.
Building Custom Synchronizers
The class libraries include a number of
state-dependent
classes—those having operations with
state-based preconditions
—such as
FutureTask
,
Semaphore
, and
BlockingQueue
.
For example, you cannot remove an item from an empty queue or retrieve the result of a task
that has not yet finished; before these operations can proceed, you must wait until the queue
enters the “nonempty” state or the task enters the “completed” state.
The easiest way to construct a state-dependent class is usually to build on top of an existing
tDownLatch
to provide the required blocking behavior. But if the library classes do not
provide the functionality you need, you can also build your own synchronizers using the
low-level mechanisms provided by the language and libraries, including intrinsic
condition
queues
, explicit
Condition
objects, and the
AbstractQueuedSynchronizer
frame-
work. This chapter explores the various options for implementing state dependence and the
rules for using the state dependence mechanisms provided by the platform.
14.1. Managing State Dependence
In a single-threaded program, if a state-based precondition (like “the connection pool is
nonempty”) does not hold when a method is called, it will never become true. Therefore,
classes in sequential programs can be coded to fail when their preconditions do not hold. But in
a concurrent program, state-based conditions can change through the actions of other threads:
a pool that was empty a few instructions ago can become nonempty because another thread
returned an element. State-dependent methods on concurrent objects can sometimes get away
with failing when their preconditions are not met, but there is often a better alternative: wait
for the precondition to become true.
State-dependent operations that
block
until the operation can proceed are more convenient and
less error-prone than those that simply fail. The built-in condition queue mechanism enables
threads to block until an object has entered a state that allows progress and to wake blocked
threads when they may be able to make further progress. We cover the details of condition
queues in
Section 14.2
,
but to motivate the value of an efficient condition wait mechanism, we
first show how state dependence might be (painfully) tackled using polling and sleeping.
A blocking state-dependent action takes the form shown in
Listing 14.1
.
The pattern of locking
is somewhat unusual in that the lock is released and reacquired in the middle of the opera-
tion. The state variables that make up the precondition must be guarded by the object's lock,