Concurrent multithreaded AQS

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
  1. isHeldExclusively()// Is the thread exclusive of resources? Only condition is needed to implement it.
  2. tryAcquire(int)// exclusive mode. Attempts to obtain resources return true for success and false for failure.
  3. tryRelease(int)// exclusive mode. Attempts to release resources return true for success and false for failure.
  4. 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.
  5. tryReleaseShared(int)// Sharing mode. Attempts to release resources return true for success and false for failure.

Keywords: Java

Added by Invincible on Thu, 01 Aug 2019 13:20:56 +0300