What is AQS
- The full name is AbstractQueued Synchronizer (abstract queue synchronizer)
- Its location phone provides a basic framework for almost all locks and synchronizers in java
- AQS is based on FIFO queue (First-In, First-Out, FIFO first-in, first-out) and maintains a state variable, which can be updated by atom (CAS) to achieve the operation of locking and unlocking.
The Realization Principle of AQS
thought
If the requested shared resource is idle, the current thread requesting the resource is set to a valid worker thread, and the shared resource is set to a locked state. If the requested shared resource is occupied, a set of thread blocking waiting and wake-up mechanisms are needed. This mechanism AQS is a CLH queue. Locks are implemented by adding threads that are temporarily unavailable to the queue.
CLH queue
CLH(Craig,Landin,and Hagersten) queue is a virtual two-way queue (virtual two-way queue means that there are no queue instances, only the correlation between nodes). AQS encapsulates each thread requesting shared resources as a node of a CLH queue to allocate locks.
Analysis of Core Source Code
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable { private static final long serialVersionUID = 7373984972572414691L; /** * Creates a new {@code AbstractQueuedSynchronizer} instance * with initial synchronization state of zero. */ protected AbstractQueuedSynchronizer() { } static final class Node { // Identifying a node as a shared mode static final Node SHARED = new Node(); // Identifying a node as mutually exclusive static final Node EXCLUSIVE = null; // Identification thread cancelled static final int CANCELLED = 1; // Identify successor nodes that need to wake up static final int SIGNAL = -1; // Identifying threads waiting on a condition static final int CONDITION = -2; // Shared locks behind the identity require unconditional propagation (shared locks require continuous wake-up threads) static final int PROPAGATE = -3; // Waiting state corresponding to threads saved by the current node volatile int waitStatus; // The former node volatile Node prev; // The latter node volatile Node next; // Threads saved by the current node volatile Thread thread; // Next node waiting on Condition (used when Condition locks) Node nextWaiter; // Is it a shared mode? final boolean isShared() { return nextWaiter == SHARED; } // Get the previous node final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } // Node Construction Method Node() { // Used to establish initial head or SHARED marker } // Node Construction Method Node(Thread thread, Node mode) { // Used by addWaiter // Store shared or mutually exclusive patterns in the next Waiter field this.nextWaiter = mode; this.thread = thread; } // Node Construction Method Node(Thread thread, int waitStatus) { // Used by Condition // The state of waiting, used in Condition s this.waitStatus = waitStatus; this.thread = thread; } } }
The bottom layer of AQS uses the template method pattern
Synchronizer design is based on the template method pattern. If you need to customize the synchronizer, the general way is this.
- Users inherit AbstractQueued Synchronizer and rewrite the specified methods (these rewriting methods are simple, nothing more than acquisition and release of contributing resource state)
- Combining AQS into the implementation of custom synchronization components and calling their template methods, which call user-rewritten methods
Several methods that need to be rewritten when customizing AQS
- isHeldExclusively()// Is the thread exclusive of resources? Only condition is needed to implement it.
- tryAcquire(int)// exclusive mode. Attempts to obtain resources return true for success and false for failure.
- tryRelease(int)// exclusive mode. Attempts to release resources return true for success and false for failure.
- tryAcquireShared(int)// Sharing mode. Try to get resources. Negative numbers denote failure; 0 denotes success, but there are no remaining available resources; positive numbers denote success, and there are remaining resources.
- tryReleaseShared(int)// Sharing mode. Attempts to release resources return true for success and false for failure.