Java Reference
In-Depth Information
like
LinkedBlockingQueue
,
CountDown-Latch
,
Semaphore
, and
FutureTask
when you can; if you can get away with it, it is a lot easier.)
14.2.1. The Condition Predicate
The key to using condition queues correctly is identifying the
condition predicates
that the
object may wait for. It is the condition predicate that causes much of the confusion surround-
ing
wait
and
notify
, because it has no instantiation in the API and nothing in either the
language specification or the JVM implementation ensures its correct use. In fact, it is not
mentioned directly at all in the language specification or the Javadoc. But without it, condi-
tion waits would not work.
The condition predicate is the precondition that makes an operation state-dependent in the
first place.
In a bounded buffer,
take
can proceed only if the buffer is not empty; otherwise
it must wait. For
take
, the condition predicate is “the buffer is not empty”, which
take
must test for before proceeding. Similarly, the condition predicate for
put
is “the buffer is
not full”. Condition predicates are expressions constructed from the state variables of the
class;
BaseBoundedBuffer
tests for “buffer not empty” by comparing
count
to zero,
and tests for “buffer not full” by comparing
count
to the buffer size.
Document the condition predicate(s) associated with a condition queue and the operations
that wait on them.
There is an important three-way relationship in a condition wait involving locking, the
wait
method, and a condition predicate. The condition predicate involves state variables, and the
state variables are guarded by a lock, so before testing the condition predicate, we must hold
that lock. The lock object and the condition queue object (the object on which
wait
and
no-
tify
are invoked) must also be the same object.
In
BoundedBuffer
, the buffer state is guarded by the buffer lock and the buffer object is
used as the condition queue. The
take
method acquires the buffer lock and then tests the
condition predicate (that the buffer is nonempty). If the buffer is indeed nonempty, it removes
the first element, which it can do because it still holds the lock guarding the buffer state.
If the condition predicate is not true (the buffer is empty),
take
must wait until another
thread puts an object in the buffer. It does this by calling
wait
on the buffer's intrinsic condi-
tion queue, which requires holding the lock on the condition queue object. As careful design