Java Reference
In-Depth Information
the other hand, if the class does not impose any such constraints, we may be able to relax en-
capsulation or serialization requirements to obtain greater flexibility or better performance.
A class can also have invariants that constrain multiple state variables. A number range class,
like NumberRange in Listing 4.10 , typically maintains state variables for the lower and up-
per bounds of the range. These variables must obey the constraint that the lower bound be
less than or equal to the upper bound. Multivariable invariants like this one create atomicity
requirements: related variables must be fetched or updated in a single atomic operation. You
cannot update one, release and reacquire the lock, and then update the others, since this could
involve leaving the object in an invalid state when the lock was released. When multiple vari-
ables participate in an invariant, the lock that guards them must be held for the duration of
any operation that accesses the related variables.
You cannot ensure thread safety without understanding an object's invariants and postcondi-
tions. Constraints on the valid values or state transitions for state variables can create atom-
icity and encapsulation requirements.
4.1.2. State-dependent Operations
Class invariants and method postconditions constrain the valid states and state transitions for
an object. Some objects also have methods with state-based preconditions . For example, you
cannot remove an item from an empty queue; a queue must be in the “nonempty” state before
you can remove an element. Operations with state-based preconditions are called state-de-
pendent [CPJ 3].
In a single-threaded program, if a precondition does not hold, the operation has no choice but
to fail. But in a concurrent program, the precondition may become true later due to the action
of another thread. Concurrent programs add the possibility of waiting until the precondition
becomes true, and then proceeding with the operation.
The built-in mechanisms for efficiently waiting for a condition to become true— wait and
notify —are tightly bound to intrinsic locking, and can be difficult to use correctly. To cre-
ate operations that wait for a precondition to become true before proceeding, it is often easier
to use existing library classes, such as blocking queues or semaphores, to provide the de-
sired state-dependent behavior. Blocking library classes such as BlockingQueue , Sema-
phore , and other synchronizers are covered in Chapter 5 ; creating state-dependent classes
Search WWH ::




Custom Search