One, foreword

  1. What is AQS (AbstractQueuedSynchronizer) translation called abstract synchronous queue, he is another except a synchronized synchronous mechanism
  2. The realization of Lock Lock depends on the use and principle of AQS later write Lock Lock
  3. The central ideas of AQS are: Site to see if the Shared resources free, free is locked if the Shared resources (modified state), thread execution of the business such as code lock is released (reset), other threads If the Shared resources have been locked (state flag bit takes up), into the waiting queue monitor status Once they are reset to grab the lock (scrambled to modify status)
  4. AQS maintains a FIFO bidirectional queue,FIFO is first in first out bidirectional is to have _pre _next two points to the attributes of the two nodes before and after

  1. 3+4 === state+ bidirectional queue

  2. AOS provides two methods to obtain lock resources: exclusive lock, such as ReentrantLock, and shared lock, such as CountDownLatch

Second, the DEMO

AQS if there is no specific implementation class, DEMO is meaningless, we first briefly look at some common methods inside it

1. Waiting in line We like stealing a glance at the beauty, so let’s see the NODE is he AbstractQueuedSynchronizer inner class, which is usually lined up individual (a beauty Have a handsome boy)


    static final class Node {
        // Mark Node for shared mode
        static final Node SHARED = new Node();
        // Mark Node as exclusive mode
        static final Node EXCLUSIVE = null;

        // Several states of waitStatus
        // The current Node is cancelled due to timeout or interruption. The Node in this state will not be blocked again
        static final int CANCELLED =  1;
        // When a node is queued, ensure that the previous node is the SIGNAL node
        static final int SIGNAL    = -1;
        // 
        static final int CONDITION = -2;
        static final int PROPAGATE = -3;
        // There is also a default of 0 stateless

      
        volatile int waitStatus;

        // The bidirectional queue points to the previous Node
        volatile Node prev;
        // The two-way queue points to the latter Nodesss
        volatile Node next;
        // Currently waiting thread!! The heart of the Node
        volatile Thread thread;

        // point to the next node waiting in the same condition
        // Or a special value: SHARED
        // The conditions queue can only be in exclusive mode if it is in SHARED mode we mark SHARED with a special value
        Node nextWaiter;

        // Whether the mode is shared
        final boolean isShared(a) {
            return nextWaiter == SHARED;
        }

        // Returns the previous Node
        final Node predecessor(a) throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }
		// when initializing head and share tags
        Node() {  
        }
		/ / addWaiter
        Node(Thread thread, Node mode) {    
            this.nextWaiter = mode;
            this.thread = thread;
        }
		/ / Condition
        Node(Thread thread, int waitStatus) {
            this.waitStatus = waitStatus;
            this.thread = thread; }}Copy the code

2. Try to ask the cashier if it’s your turn when tryAcquire comes on.

 /** * Attempts to acquire in exclusive mode. This method should query * if the state of the object permits it to be Acquired in the * exclusive mode, and if so to acquire it. Get * < P >This method is always invoked by the Thread performing * acquire. If This method reports failure, the acquire method * may queue the thread, if it is not already queued, until it is * signalled by a release from some other thread. This can be used * to implement method {@linkLock#tryLock()}. * If the fetch fails, the current thread will be queued until it is woken up by another release thread * <p>The default * implementation throws {@link UnsupportedOperationException}.
     *
     * @paramArg the acquire argument. * This value is normally 1 * *@return {@codeTrue} if successful. Upon success, this object has * been acquired
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }
Copy the code

What the hell? Just throw me an exception? AQS is just an idea, and there is only one process in its class that is not implemented.

What model? Is it similar to the template method

Let’s find an implementation ReentrantLock#FairSync#tryAcquire and take a look at the class diagram

 /** * Sync object for fair locks */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
		/ / lock method
        final void lock(a) {
            acquire(1);
        }

        /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. * The waiters/tryAcquire server loves his work, but he loves his work recursively, or loves his work first, he loves his work recursively, he loves his work first, he loves his work recursively, he loves his work first, he loves his work backwards, he loves his work backwards, he loves his work backwards, he loves his work backwards
        protected final boolean tryAcquire(int acquires) {
            // Get the current thread
            final Thread current = Thread.currentThread();
            This is one of the two cores: state bit state
            // This directly calls the parent AQS getState() method
            // Advantages of inheritance: You have what dad has
            int c = getState();
            // If the value is 0, no one is in possession
            if (c == 0) {
                Hasqueuedtoraise: Determines whether another thread waits longer than the current thread
                // 2.compareAndSetState: CAS thread safe setting State 0->acquires
                // 
                if(! hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
                    // Set the exclusive thread to the current thread
                    setExclusiveOwnerThread(current);
                    return true; }}// If state is not equal to 0, see if the current thread is occupying state
            else if (current == getExclusiveOwnerThread()) {
                // If the current thread occupies state then the state total has been reentered +acquires
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            // Return false if there is no fetch
            return false; }}Copy the code

A: Do you want to tryAcquire the lock

For example, when you go shopping to buy a pancake, there is a long queue in front of the pancake stall. When you look behind you, you cannot find yourself, what should you do?

For the girlfriend who loves to eat pancakes, of course, you need to queue up

AbstractQueuedSynchronizer#acquire

   Implemented by invoking at least once {/** * Implemented in exclusive mode, ignoring interrupts.@link #tryAcquire},
        * returning on success. 
        * Otherwise the thread is queued, possibly
        * repeatedly blocking and unblocking, invoking {@link* #tryAcquire} until success. * call tryAcquire at least once, if successful, and if not, put the current thread on the wait queue */
       public final void acquire(int arg) {
           // tryAcquire see 2 examples
           // addWaiter(exclusive join queue) AQS method
           // acquireQueued This is an endless loop until the lock is acquired and the cookie is bought
           if(! tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }Copy the code

AbstractQueuedSynchronizer#addWaiter

   	/** * Creates and enqueues nodes for current thread and given mode. You become part of a queue * in reality you may be given a number or a flyer or a snack to eat first *@param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
        * @return the new node
        */
       private Node addWaiter(Node mode) {
           / / packaging
           Node node = new Node(Thread.currentThread(), mode);
           // Try the fast path of enq; backup to full enq on failure
           // Get the last element of the queue
           Node pred = tail; 
           // Append if the tail element is not null
           // Add a new element to the cas queue by tail
           if(pred ! =null) {X
               // Set the preceding node of the currently inserted node to tail
               node.prev = pred;
               // cas sets tail to the element just added
               if (compareAndSetTail(pred, node)) {
                   // The next element of the old tail points to the new node
                   pred.next = node;
                   returnnode; }}// If it is null, it is normal to initialize the queue
           enq(node);
           return node;
       }
   
   	/** * Inserts node into queue, initializing if necessary * Inserts nodes into the queue, queue initialization if necessary *@param node the node to insert
        * @return node's predecessor
        */
       private Node enq(final Node node) {
           // Wait until you succeed
           for (;;) {
               Node t = tail;
               // If null is used for initialization
               if (t == null) {
                   // Set a new Node to head and tail to point to the Node
                   if (compareAndSetHead(new Node()))
                       tail = head;
               } else {
                   // Set an element on node to t (the empty node you just added)
                   node.prev = t;
                   // cas sets tail to node
                   if (compareAndSetTail(t, node)) {
                       t.next = node;
                       returnt; }}}}Copy the code
     /** * Acquires in exclusive uninterruptible mode for thread already in * queue. Used by condition wait methods as well As acquire. * Threads in the queue apply for locks in exclusive and uninterrupted mode *@param node the node
     * @param arg the acquire argument
     * @return {@code true} if interrupted while waiting
     */
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            // Interrupt is set to false
            boolean interrupted = false;
            for (;;) {
                // Get the previous Node of the current Node
                final Node p = node.predecessor();
                // If the previous Node is head, the xiaoqiang buys a pancake
                if (p == head && tryAcquire(arg)) {
                    // Set Roach to head so that his next person knows his turn
                    setHead(node);
                    / / to speed up the gc
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // If no fetch continues
                / / 1. ShouldParkAfterFailedAcquire check status If the status is right park
                //2. ParkAndCheckInterrupt Wait a moment
                //3. Wait for the unpark bar to call you when the person in front of you finishes buying
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true; }}finally {
            // The failure ended because of some exception but did not succeed
            // Disqualify the lock
            // If xiao Yueyue has a stomachache, let him go. He is no longer qualified to eat pancakes
            // Set the next of the person before xiao Yueyue to the person after Xiao Yueyue
            // Then set the prev of the next person to that of the previous person
            // Analogy: delete a list
            if(failed) cancelAcquire(node); }}/** * Checks and updates status for a node that failed to acquire. * /** Checks and updates status for a node that failed to acquire This is the main signal * If node needs to block return true * Control in all acquire loops. Requires that pred == node.prev. * *@paramPred the preceding node *@paramNode Indicates the current node *@return {@codeTrue} Returns true */ if it can be blocked
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        // 
        if (ws == Node.SIGNAL)
            // SIGNAL state can be safe park
            return true;
        if (ws > 0) {
            /* * Predecessor was cancelled. Skip over installations and * indicate retry. * If the state is greater than 0, it is required to find a legitimate node * That is, next yueyue has a stomachache and needs to make the next person in front of him become the person behind xiao Yueyue * similar to deleting linked list nodes */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            // Set the next of a valid node to the current node
            pred.next = node;
        } else {
            /* * waitStatus must be 0 or PROPAGATE. Indicate that we * need a signal, but don't park yet. Caller will need to * retry to make sure it cannot acquire before parking. */
            // Set the status to SIGNAL
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }


   /** * Convenience method to park and then check if interrupted * park@return {@code true} if interrupted
     */
    private final boolean parkAndCheckInterrupt(a) {
        // Park is an analogy to wait
        LockSupport.park(this);
        // We need to see if the park process is interrupted
        return Thread.interrupted();
    }
Copy the code

4. New Node() -> tryAcquire ->tryRelease jack –> Queue to buy cookies –> Go away and notify the next person to buy cookies


/**
	* Releases in exclusive mode.  Implemented by unblocking one or
     * more threads if {@link #tryRelease} returns true.
     * This method can be used to implement method {@link Lock#unlock}.
     *
     * @param arg the release argument.  This value is conveyed to
     *        {@link #tryRelease} but is otherwise uninterpreted and
     *        can represent anything you like.
     * @return the value returned from {@link #tryRelease}
     */
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        // Notify the next Node if head is not empty and waitStatus is not 0
        // When you're done, tell the person behind you watching Tiktok to buy it
        if(h ! =null&& h.waitStatus ! =0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

protected final boolean tryRelease(int releases) {
     // state- The number of releases to be made
     int c = getState() - releases;
     // If the owning thread is not the current thread, throw an exception
     if(Thread.currentThread() ! = getExclusiveOwnerThread())throw new IllegalMonitorStateException();
     // 
     boolean free = false;
     // if c ==0 then lock lock release release is done
     if (c == 0) {
         free = true;
         // Set the owning thread to NULL
         setExclusiveOwnerThread(null);
     }
     // Set the status bit == 0
     setState(c);
     return free;
 }

 /** * Wakes up node's antecedents, if one exists. * If the next node exists *@param node the node
     */
    private void unparkSuccessor(Node node) {
      
        // Set the current Node state to 0
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */
        // loop to find a valid next
        '// The cancelled version has been cancelled because he is xiao Yueyue and has a bad stomach
        // Then wake him up and let him poop and see if the next one is legal until we find one who is
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for(Node t = tail; t ! =null&& t ! = node; t = t.prev)if (t.waitStatus <= 0)
                    s = t;
        }
        / / wake unpark
        if(s ! =null)
            LockSupport.unpark(s.thread);
    }
Copy the code

Pretend academic discussions

  1. Originally quite clear, looked at the code, feel into the mountain, that sentence is good: only fate body in this mountain

    Take a full view of the mountain:

  1. After I read it, I didn’t think there was much going on

    (1) Try to obtain the lock

    (2) If you are the first to initialize the list to get the lock

    (3) If you are not the first to join the queue and wait to be called

    (4) Being woken up to perform the operation

    (5) Release the lock after the operation and call the next person to execute it

This article has many pictures, if not clear, you can follow the public account: Muzi day and night

Send "AQS" to get the hd map visit the address send "Directions" to get the outline of this series of articles and send me your questions, I will reply as soon as I see themCopy the code

Finally, I attached my own public account just began to write wish to progress together:

Note: the above text only represents personal views, only for reference, if there is a problem, please point out immediately even roll out of bed to correct.