public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable
int value to represent state. Subclasses
must define the protected methods that change this state, and which
define what that state means in terms of this object being acquired
or released. Given these, the other methods in this class carry
out all queuing and blocking mechanics. Subclasses can maintain
other state fields, but only the atomically updated int
value manipulated using methods getState(), setState(int) and compareAndSetState(int, int) is tracked with respect
to synchronization.
Subclasses should be defined as non-public internal helper
classes that are used to implement the synchronization properties
of their enclosing class. Class
AbstractQueuedSynchronizer does not implement any
synchronization interface. Instead it defines methods such as
acquireInterruptibly(int) that can be invoked as
appropriate by concrete locks and related synchronizers to
implement their public methods.
This class supports either or both a default exclusive
mode and a shared mode. When acquired in exclusive mode,
attempted acquires by other strands cannot succeed. Shared mode
acquires by multiple strands may (but need not) succeed. This class
does not "understand" these differences except in the
mechanical sense that when a shared mode acquire succeeds, the next
waiting strand (if one exists) must also determine whether it can
acquire as well. Strands waiting in the different modes share the
same FIFO queue. Usually, implementation subclasses support only
one of these modes, but both can come into play for example in a
ReadWriteLock. Subclasses that support only exclusive or
only shared modes need not define the methods supporting the unused mode.
This class defines a nested AbstractQueuedSynchronizer.ConditionObject class that
can be used as a Condition implementation by subclasses
supporting exclusive mode for which method isHeldExclusively() reports whether synchronization is exclusively
held with respect to the current strand, method release(int)
invoked with the current getState() value fully releases
this object, and acquire(int), given this saved state value,
eventually restores this object to its previous acquired state. No
AbstractQueuedSynchronizer method otherwise creates such a
condition, so if this constraint cannot be met, do not use it. The
behavior of AbstractQueuedSynchronizer.ConditionObject depends of course on the
semantics of its synchronizer implementation.
This class provides inspection, instrumentation, and monitoring
methods for the internal queue, as well as similar methods for
condition objects. These can be exported as desired into classes
using an AbstractQueuedSynchronizer for their
synchronization mechanics.
Serialization of this class stores only the underlying atomic
integer maintaining state, so deserialized objects have empty
strand queues. Typical subclasses requiring serializability will
define a readObject method that restores this to a known
initial state upon deserialization.
To use this class as the basis of a synchronizer, redefine the
following methods, as applicable, by inspecting and/or modifying
the synchronization state using getState(), setState(int) and/or compareAndSetState(int, int):
UnsupportedOperationException. Implementations of these methods
must be internally strand-safe, and should in general be short and
not block. Defining these methods is the only supported
means of using this class. All other methods are declared
final because they cannot be independently varied.
You may also find the inherited methods from AbstractOwnableSynchronizer useful to keep track of the strand
owning an exclusive synchronizer. You are encouraged to use them
-- this enables monitoring and diagnostic tools to assist users in
determining which strands hold locks.
Even though this class is based on an internal FIFO queue, it does not automatically enforce FIFO acquisition policies. The core of exclusive synchronization takes the form:
Acquire:
while (!tryAcquire(arg)) {
enqueue strand if it is not already queued;
possibly block current strand;
}
Release:
if (tryRelease(arg))
unblock the first queued strand;
(Shared mode is similar but may involve cascading signals.)
Because checks in acquire are invoked before
enqueuing, a newly acquiring strand may barge ahead of
others that are blocked and queued. However, you can, if desired,
define tryAcquire and/or tryAcquireShared to
disable barging by internally invoking one or more of the inspection
methods, thereby providing a fair FIFO acquisition order.
In particular, most fair synchronizers can define tryAcquire
to return false if hasQueuedPredecessors() (a method
specifically designed to be used by fair synchronizers) returns
true. Other variations are possible.
Throughput and scalability are generally highest for the
default barging (also known as greedy,
renouncement, and convoy-avoidance) strategy.
While this is not guaranteed to be fair or starvation-free, earlier
queued strands are allowed to recontend before later queued
strands, and each recontention has an unbiased chance to succeed
against incoming strands. Also, while acquires do not
"spin" in the usual sense, they may perform multiple
invocations of tryAcquire interspersed with other
computations before blocking. This gives most of the benefits of
spins when exclusive synchronization is only briefly held, without
most of the liabilities when it isn't. If so desired, you can
augment this by preceding calls to acquire methods with
"fast-path" checks, possibly prechecking hasContended()
and/or hasQueuedStrands() to only do so if the synchronizer
is likely not to be contended.
int state, acquire, and
release parameters, and an internal FIFO wait queue. When this does
not suffice, you can build synchronizers from a lower level using
atomic classes, your own custom
Queue classes, and LockSupport blocking
support.
class Mutex implements Lock, java.io.Serializable {
// Our internal helper class
private static class Sync extends AbstractQueuedSynchronizer {
// Reports whether in locked state
protected boolean isHeldExclusively() {
return getState() == 1;
}
// Acquires the lock if state is zero
public boolean tryAcquire(int acquires) {
assert acquires == 1; // Otherwise unused
if (compareAndSetState(0, 1)) {
setExclusiveOwnerStrand(Strand.currentStrand());
return true;
}
return false;
}
// Releases the lock by setting state to zero
protected boolean tryRelease(int releases) {
assert releases == 1; // Otherwise unused
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerStrand(null);
setState(0);
return true;
}
// Provides a Condition
Condition newCondition() { return new ConditionObject(); }
// Deserializes properly
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
// The sync object does all the hard work. We just forward to it.
private final Sync sync = new Sync();
public void lock() { sync.acquire(1); }
public boolean tryLock() { return sync.tryAcquire(1); }
public void unlock() { sync.release(1); }
public Condition newCondition() { return sync.newCondition(); }
public boolean isLocked() { return sync.isHeldExclusively(); }
public boolean hasQueuedStrands() { return sync.hasQueuedStrands(); }
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}
Here is a latch class that is like a
CountDownLatch
except that it only requires a single signal to
fire. Because a latch is non-exclusive, it uses the shared
acquire and release methods.
class BooleanLatch {
private static class Sync extends AbstractQueuedSynchronizer {
boolean isSignalled() { return getState() != 0; }
protected int tryAcquireShared(int ignore) {
return isSignalled() ? 1 : -1;
}
protected boolean tryReleaseShared(int ignore) {
setState(1);
return true;
}
}
private final Sync sync = new Sync();
public boolean isSignalled() { return sync.isSignalled(); }
public void signal() { sync.releaseShared(1); }
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
}| Modifier and Type | Class and Description |
|---|---|
class |
AbstractQueuedSynchronizer.ConditionObject
Condition implementation for a
AbstractQueuedSynchronizer serving as the basis of a Lock implementation. |
| Modifier | Constructor and Description |
|---|---|
protected |
AbstractQueuedSynchronizer()
Creates a new
AbstractQueuedSynchronizer instance
with initial synchronization state of zero. |
| Modifier and Type | Method and Description |
|---|---|
void |
acquire(int arg)
Acquires in exclusive mode, ignoring interrupts.
|
void |
acquireInterruptibly(int arg)
Acquires in exclusive mode, aborting if interrupted.
|
void |
acquireShared(int arg)
Acquires in shared mode, ignoring interrupts.
|
void |
acquireSharedInterruptibly(int arg)
Acquires in shared mode, aborting if interrupted.
|
protected boolean |
compareAndSetState(int expect,
int update)
Atomically sets synchronization state to the given updated
value if the current state value equals the expected value.
|
java.util.Collection<Strand> |
getExclusiveQueuedStrands()
Returns a collection containing strands that may be waiting to
acquire in exclusive mode.
|
Strand |
getFirstQueuedStrand()
Returns the first (longest-waiting) strand in the queue, or
null if no strands are currently queued. |
java.util.Collection<Strand> |
getQueuedStrands()
Returns a collection containing strands that may be waiting to
acquire.
|
int |
getQueueLength()
Returns an estimate of the number of strands waiting to
acquire.
|
java.util.Collection<Strand> |
getSharedQueuedStrands()
Returns a collection containing strands that may be waiting to
acquire in shared mode.
|
protected int |
getState()
Returns the current value of synchronization state.
|
java.util.Collection<Strand> |
getWaitingStrands(AbstractQueuedSynchronizer.ConditionObject condition)
Returns a collection containing those strands that may be
waiting on the given condition associated with this
synchronizer.
|
int |
getWaitQueueLength(AbstractQueuedSynchronizer.ConditionObject condition)
Returns an estimate of the number of strands waiting on the
given condition associated with this synchronizer.
|
boolean |
hasContended()
Queries whether any strands have ever contended to acquire this
synchronizer; that is if an acquire method has ever blocked.
|
boolean |
hasQueuedPredecessors()
Queries whether any strands have been waiting to acquire longer
than the current strand.
|
boolean |
hasQueuedStrands()
Queries whether any strands are waiting to acquire.
|
boolean |
hasWaiters(AbstractQueuedSynchronizer.ConditionObject condition)
Queries whether any strands are waiting on the given condition
associated with this synchronizer.
|
protected boolean |
isHeldExclusively()
Returns
true if synchronization is held exclusively with
respect to the current (calling) strand. |
boolean |
isQueued(Strand strand)
Returns true if the given strand is currently queued.
|
boolean |
owns(AbstractQueuedSynchronizer.ConditionObject condition)
Queries whether the given ConditionObject
uses this synchronizer as its lock.
|
boolean |
release(int arg)
Releases in exclusive mode.
|
boolean |
releaseShared(int arg)
Releases in shared mode.
|
protected void |
setState(int newState)
Sets the value of synchronization state.
|
java.lang.String |
toString()
Returns a string identifying this synchronizer, as well as its state.
|
protected boolean |
tryAcquire(int arg)
Attempts to acquire in exclusive mode.
|
boolean |
tryAcquireNanos(int arg,
long nanosTimeout)
Attempts to acquire in exclusive mode, aborting if interrupted,
and failing if the given timeout elapses.
|
protected int |
tryAcquireShared(int arg)
Attempts to acquire in shared mode.
|
boolean |
tryAcquireSharedNanos(int arg,
long nanosTimeout)
Attempts to acquire in shared mode, aborting if interrupted, and
failing if the given timeout elapses.
|
protected boolean |
tryRelease(int arg)
Attempts to set the state to reflect a release in exclusive
mode.
|
protected boolean |
tryReleaseShared(int arg)
Attempts to set the state to reflect a release in shared mode.
|
getExclusiveOwnerStrand, setExclusiveOwnerStrandprotected AbstractQueuedSynchronizer()
AbstractQueuedSynchronizer instance
with initial synchronization state of zero.protected final int getState()
volatile read.protected final void setState(int newState)
volatile write.newState - the new state valueprotected final boolean compareAndSetState(int expect,
int update)
volatile read
and write.expect - the expected valueupdate - the new valuetrue if successful. False return indicates that the actual
value was not equal to the expected value.protected boolean tryAcquire(int arg)
This method is always invoked by the strand performing
acquire. If this method reports failure, the acquire method
may queue the strand, if it is not already queued, until it is
signalled by a release from some other strand. This can be used
to implement method Lock#tryLock().
The default
implementation throws UnsupportedOperationException.
arg - the acquire argument. This value is always the one
passed to an acquire method, or is the value saved on entry
to a condition wait. The value is otherwise uninterpreted
and can represent anything you like.true if successful. Upon success, this object has
been acquired.java.lang.IllegalMonitorStateException - if acquiring would place this
synchronizer in an illegal state. This exception must be
thrown in a consistent fashion for synchronization to work
correctly.java.lang.UnsupportedOperationException - if exclusive mode is not supportedprotected boolean tryRelease(int arg)
This method is always invoked by the strand performing release.
The default implementation throws
UnsupportedOperationException.
arg - the release argument. This value is always the one
passed to a release method, or the current state value upon
entry to a condition wait. The value is otherwise
uninterpreted and can represent anything you like.true if this object is now in a fully released
state, so that any waiting strands may attempt to acquire;
and false otherwise.java.lang.IllegalMonitorStateException - if releasing would place this
synchronizer in an illegal state. This exception must be
thrown in a consistent fashion for synchronization to work
correctly.java.lang.UnsupportedOperationException - if exclusive mode is not supportedprotected int tryAcquireShared(int arg)
This method is always invoked by the strand performing acquire. If this method reports failure, the acquire method may queue the strand, if it is not already queued, until it is signalled by a release from some other strand.
The default implementation throws UnsupportedOperationException.
arg - the acquire argument. This value is always the one
passed to an acquire method, or is the value saved on entry
to a condition wait. The value is otherwise uninterpreted
and can represent anything you like.java.lang.IllegalMonitorStateException - if acquiring would place this
synchronizer in an illegal state. This exception must be
thrown in a consistent fashion for synchronization to work
correctly.java.lang.UnsupportedOperationException - if shared mode is not supportedprotected boolean tryReleaseShared(int arg)
This method is always invoked by the strand performing release.
The default implementation throws
UnsupportedOperationException.
arg - the release argument. This value is always the one
passed to a release method, or the current state value upon
entry to a condition wait. The value is otherwise
uninterpreted and can represent anything you like.true if this release of shared mode may permit a
waiting acquire (shared or exclusive) to succeed; and
false otherwisejava.lang.IllegalMonitorStateException - if releasing would place this
synchronizer in an illegal state. This exception must be
thrown in a consistent fashion for synchronization to work
correctly.java.lang.UnsupportedOperationException - if shared mode is not supportedprotected boolean isHeldExclusively()
true if synchronization is held exclusively with
respect to the current (calling) strand. This method is invoked
upon each call to a non-waiting AbstractQueuedSynchronizer.ConditionObject method.
(Waiting methods instead invoke release(int).)
The default implementation throws UnsupportedOperationException. This method is invoked
internally only within AbstractQueuedSynchronizer.ConditionObject methods, so need
not be defined if conditions are not used.
true if synchronization is held exclusively;
false otherwisejava.lang.UnsupportedOperationException - if conditions are not supported@Suspendable public final void acquire(int arg)
tryAcquire(int),
returning on success. Otherwise the strand is queued, possibly
repeatedly blocking and unblocking, invoking tryAcquire(int) until success. This method can be used
to implement method Lock#lock.arg - the acquire argument. This value is conveyed to
tryAcquire(int) but is otherwise uninterpreted and
can represent anything you like.@Suspendable public final void acquireInterruptibly(int arg) throws java.lang.InterruptedException
tryAcquire(int), returning on
success. Otherwise the strand is queued, possibly repeatedly
blocking and unblocking, invoking tryAcquire(int)
until success or the strand is interrupted. This method can be
used to implement method Lock#lockInterruptibly.arg - the acquire argument. This value is conveyed to
tryAcquire(int) but is otherwise uninterpreted and
can represent anything you like.java.lang.InterruptedException - if the current strand is interrupted@Suspendable public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws java.lang.InterruptedException
tryAcquire(int), returning on success. Otherwise, the strand is
queued, possibly repeatedly blocking and unblocking, invoking
tryAcquire(int) until success or the strand is interrupted
or the timeout elapses. This method can be used to implement
method Lock#tryLock(long, TimeUnit).arg - the acquire argument. This value is conveyed to
tryAcquire(int) but is otherwise uninterpreted and
can represent anything you like.nanosTimeout - the maximum number of nanoseconds to waittrue if acquired; false if timed outjava.lang.InterruptedException - if the current strand is interruptedpublic final boolean release(int arg)
tryRelease(int) returns true.
This method can be used to implement method Lock#unlock.arg - the release argument. This value is conveyed to
tryRelease(int) but is otherwise uninterpreted and
can represent anything you like.tryRelease(int)@Suspendable public final void acquireShared(int arg)
tryAcquireShared(int),
returning on success. Otherwise the strand is queued, possibly
repeatedly blocking and unblocking, invoking tryAcquireShared(int) until success.arg - the acquire argument. This value is conveyed to
tryAcquireShared(int) but is otherwise uninterpreted
and can represent anything you like.@Suspendable public final void acquireSharedInterruptibly(int arg) throws java.lang.InterruptedException
tryAcquireShared(int), returning on success. Otherwise the
strand is queued, possibly repeatedly blocking and unblocking,
invoking tryAcquireShared(int) until success or the strand
is interrupted.arg - the acquire argument.
This value is conveyed to tryAcquireShared(int) but is
otherwise uninterpreted and can represent anything
you like.java.lang.InterruptedException - if the current strand is interrupted@Suspendable public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws java.lang.InterruptedException
tryAcquireShared(int), returning on success. Otherwise, the
strand is queued, possibly repeatedly blocking and unblocking,
invoking tryAcquireShared(int) until success or the strand
is interrupted or the timeout elapses.arg - the acquire argument. This value is conveyed to
tryAcquireShared(int) but is otherwise uninterpreted
and can represent anything you like.nanosTimeout - the maximum number of nanoseconds to waittrue if acquired; false if timed outjava.lang.InterruptedException - if the current strand is interruptedpublic final boolean releaseShared(int arg)
tryReleaseShared(int) returns true.arg - the release argument. This value is conveyed to
tryReleaseShared(int) but is otherwise uninterpreted
and can represent anything you like.tryReleaseShared(int)public final boolean hasQueuedStrands()
true return does not guarantee that any
other strand will ever acquire.
In this implementation, this operation returns in constant time.
true if there may be other strands waiting to acquirepublic final boolean hasContended()
In this implementation, this operation returns in constant time.
true if there has ever been contentionpublic final Strand getFirstQueuedStrand()
null if no strands are currently queued.
In this implementation, this operation normally returns in constant time, but may iterate upon contention if other strands are concurrently modifying the queue.
null if no strands are currently queuedpublic final boolean isQueued(Strand strand)
This implementation traverses the queue to determine presence of the given strand.
strand - the strandtrue if the given strand is on the queuejava.lang.NullPointerException - if the strand is nullpublic final boolean hasQueuedPredecessors()
An invocation of this method is equivalent to (but may be more efficient than):
getFirstQueuedStrand() != Strand.currentStrand() &&
hasQueuedStrands()
Note that because cancellations due to interrupts and
timeouts may occur at any time, a true return does not
guarantee that some other strand will acquire before the current
strand. Likewise, it is possible for another strand to win a
race to enqueue after this method has returned false,
due to the queue being empty.
This method is designed to be used by a fair synchronizer to
avoid barging.
Such a synchronizer's tryAcquire(int) method should return
false, and its tryAcquireShared(int) method should
return a negative value, if this method returns true
(unless this is a reentrant acquire). For example, the tryAcquire method for a fair, reentrant, exclusive mode
synchronizer might look like this:
protected boolean tryAcquire(int arg) {
if (isHeldExclusively()) {
// A reentrant acquire; increment hold count
return true;
} else if (hasQueuedPredecessors()) {
return false;
} else {
// try to acquire normally
}
}true if there is a queued strand preceding the
current strand, and false if the current strand
is at the head of the queue or the queue is emptypublic final int getQueueLength()
public final java.util.Collection<Strand> getQueuedStrands()
public final java.util.Collection<Strand> getExclusiveQueuedStrands()
getQueuedStrands() except that it only returns
those strands waiting due to an exclusive acquire.public final java.util.Collection<Strand> getSharedQueuedStrands()
getQueuedStrands() except that it only returns
those strands waiting due to a shared acquire.public java.lang.String toString()
"State ="
followed by the current value of getState(), and either
"nonempty" or "empty" depending on whether the
queue is empty.toString in class java.lang.Objectpublic final boolean owns(AbstractQueuedSynchronizer.ConditionObject condition)
condition - the conditiontrue if ownedjava.lang.NullPointerException - if the condition is nullpublic final boolean hasWaiters(AbstractQueuedSynchronizer.ConditionObject condition)
true return
does not guarantee that a future signal will awaken
any strands. This method is designed primarily for use in
monitoring of the system state.condition - the conditiontrue if there are any waiting strandsjava.lang.IllegalMonitorStateException - if exclusive synchronization
is not heldjava.lang.IllegalArgumentException - if the given condition is
not associated with this synchronizerjava.lang.NullPointerException - if the condition is nullpublic final int getWaitQueueLength(AbstractQueuedSynchronizer.ConditionObject condition)
condition - the conditionjava.lang.IllegalMonitorStateException - if exclusive synchronization
is not heldjava.lang.IllegalArgumentException - if the given condition is
not associated with this synchronizerjava.lang.NullPointerException - if the condition is nullpublic final java.util.Collection<Strand> getWaitingStrands(AbstractQueuedSynchronizer.ConditionObject condition)
condition - the conditionjava.lang.IllegalMonitorStateException - if exclusive synchronization
is not heldjava.lang.IllegalArgumentException - if the given condition is
not associated with this synchronizerjava.lang.NullPointerException - if the condition is null