Exclusion techniques are mainly concerned with maintaining invariants. State-dependent concurrency control imposes additional concerns surrounding preconditions and postconditions. Actions may have state-based preconditions that need not always hold when clients invoke methods on the host object. Conversely, actions may have postconditions that are unattainable when the host object is not in a proper state, when the actions of other objects it relies on fail to achieve their own postconditions, or when the actions of other threads have changed the states of other objects being relied on.
Scope of study
There are two general approaches to the design and implementation of any state-dependent action, that stem from liveness-first versus safety-first design perspectives: Optimistic try-and-see methods can always be tried when invoked, but do not always succeed, and thus may have to deal with failure (Carey and Livny 1999 122-133). Conservative check-and-act methods refuse to proceed unless preconditions hold. When preconditions do hold, the actions always succeed. If methods check neither their preconditions nor their postconditions, they can be called only in contexts in which the preconditions are somehow known to hold. Reliance on such practices in concurrent systems is problematic at best.
Optimistic and conservative approaches are about equally prevalent, and appropriate forms of them may be equally good or bad with respect to various design forces. But since their general forms are governed by issues that may be outside of your control, the two are not always interchangeable. Optimistic approaches rely on the existence of exceptions and related mechanism that indicate when postconditions do not hold.
An historic context
Dealing with Failure
Pure optimistic control designs originate from optimistic update and transaction protocols. However, optimistic methods always check postconditions (often by catching failure exceptions) and, if they fail to hold, apply a chosen failure policy. The need for try-and-see approaches usually stems from inability or unwillingness to check preconditions and related constraints. This can arise in the following ways:
Some conditions cannot be computed using the constructs available in a given language or execution context. For example, it is not possible to check whether a given lock is being held or a given reference is unique (see 2.3).
In concurrent programs, preconditions may have temporal scopes (in which case they are sometimes called activation constraints). If a constraint is not under the control of the host object, then even if it is known to hold momentarily, it need not hold throughout the course of an action relying on it. For example, your pencil may break while you are writing a message. A file system that is known at entry to a method to have enough space to write a file may run out of space (due to the actions of other independent programs) before the method finishes writing the file. Similarly, the fact that a given remote machine is currently available says nothing about whether it will crash or become unreachable in the course of a method relying on ...