For example, here is a minimal Mutex class, that uses synchronization state zero to mean unlocked, and one to mean locked。 This class does not need the value arguments supported for synchronization methods, so uses zero, and otherwise ignores them。
class Mutex { class Sync
extends AbstractQueuedSynchronizer { public boolean tryAcquire(int ignore) {
return compareAndSetState(0, 1);
}
public boolean tryRelease(int ignore) { setState(0); return true;
}
other intrinsically transient bookkeeping。 Even so, most synchronizer classes merely reset synchronization state to initial values on deserialization, in keeping with the implicit policy of built-in locks of always deserializing to an unlocked state。 This amounts to a no-op, but must still be explicitly supported to enable deserialization of final fields。
4。1Controlling Fairness
Even though they are based on FIFO queues, synchronizers are not necessarily fair。 Notice that in the basic acquire algorithm (Section 3。3), tryAcquire checks are performed before queuing。 Thus a newly acquiring thread can “steal” access that is "intended" for the first thread at the head of the queue。
This barging FIFO strategy generally provides higher aggregate throughput than other techniques。 It reduces the time during which a contended lock is available but no thread has it because the intended next thread is in the process of unblocking。 At the same time, it avoids excessive, unproductive contention by only allowing one (the first) queued thread to wake up and try to acquire upon any release。 Developers creating synchronizers may further accentuate barging effects in cases where synchronizers are expected to be held only briefly by defining tryAcquire to itself retry a few times before passing back control。
Barging FIFO synchronizers have only probablistic fairness properties。 An unparked thread at the head of the lock queue has
barging thread
}
private final Sync sync = new Sync(); public void lock() { sync。acquire(0); } public void unlock() { sync。release(0); }
}
。。。
queued threads
A fuller version of this example, along with other usage guidance may be found in the J2SE documentation。 Many variants are of course possible。 For example, tryAcquire could employ "test- and-test-and-set" by checking the state value before trying to change it。
It may be surprising that a construct as performance-sensitive as a mutual exclusion lock is intended to be defined using a combination of delegation and virtual methods。 However, these are the sorts of OO design constructions that modern dynamic compilers have long focussed on。 They tend to be good at optimizing away this overhead, at least in code in which synchronizers are invoked frequently。
Class AbstractQueuedSynchronizer also supplies a number of methods that assist synchronizer classes in policy control。 For example, it includes timeout and interruptible versions of the basic acquire method。 And while discussion so far has focussed on exclusive-mode synchronizers such as locks, the AbstractQueuedSynchronizer class also contains a parallel set of methods (such as acquireShared) that differ in that the tryAcquireShared and tryReleaseShared methods can inform the framework (via their return values) that further acquires may be possible, ultimately causing it to wake up multiple threads by cascading signals。
Although it is not usually sensible to serialize (persistently store or transmit) a synchronizer, these classes are often used in turn to construct other classes, such as thread-safe collections, that are commonly serialized。 The AbstractQueuedSynchronizer and ConditionObject classes provide methods to serialize synchronization state, but not the underlying blocked threads or