3.14 Principle Analysis of Java AQS

3.14 AQS

Invincible i'm back

       AQS is the abbreviation of AbstractQueuedSynchronizer in Java . AQS is so famous that its full name is often forgotten. Let’s take a look at what AQS is.

       Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that rely on first-in-first-out (FIFO) wait queues. This class is designed to be a useful basis for most kinds of synchronizers that rely on a single atomic {@code int} value to represent state.1

       AQS provides a framework for implementing locks and corresponding synchronization through a first-in, first-out waiting queue. AQS relies on an atomic value to represent the state to build the basis of synchronous operation.



3.14.1 AQS inheritance relationship

       AbstractQueuedSynchronizer (AQS) inherits AbstractOwnableSynchronizer (AOS), let's first look at what AOS is.
Insert picture description here


3.14.2 AbstractOwnableSynchronizer

       AOS has only one private exclusiveOwnerThread thread attribute, which represents the exclusive thread currently running, and its subclasses can only access this attribute through the protected get and set methods.

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {
    
    
    private static final long serialVersionUID = 3737899427754241961L;
    protected AbstractOwnableSynchronizer() {
    
     }
    private transient Thread exclusiveOwnerThread;
    protected final void setExclusiveOwnerThread(Thread thread) {
    
    
        exclusiveOwnerThread = thread;
    }
    protected final Thread getExclusiveOwnerThread() {
    
    
        return exclusiveOwnerThread;
    }
}



3.14.3 AQS CLH queue

       CLH queue is a kind of spin lock queue. What is the meaning of spin lock queue? When the head node thread is running, its subsequent node threads are in a spinning state.

Insert picture description here

       The CLH queue Node source code is actually very simple inside, a doubly linked list, each node has a thread and waitStatus that represents the status of the thread. There is also the only non-volatile modified nextWaiter attribute. When the attribute Node is a SHARED node of this type of attribute, it means sharing. The threads on this SHARED node are sharable and there is no exclusivity, so there is no need to modify with the volatile keyword. .

static final class Node {
    
    
    static final Node SHARED = new Node();
    static final Node EXCLUSIVE = null;

    /** waitStatus value to indicate thread has cancelled */
    static final int CANCELLED =  1;
    /** waitStatus value to indicate successor's thread needs unparking */
    static final int SIGNAL    = -1;
    /** waitStatus value to indicate thread is waiting on condition */
    static final int CONDITION = -2;
    /**
     * waitStatus value to indicate the next acquireShared should
     * unconditionally propagate
     */
    static final int PROPAGATE = -3;
    volatile int waitStatus;
     * head only as a result of successful acquire. A
     * cancelled thread never succeeds in acquiring, and a thread only
     * cancels itself, not any other node.
     */
    volatile Node prev;
    volatile Node next;
    volatile Thread thread;
    Node nextWaiter;
    final boolean isShared() {
    
    
        return nextWaiter == SHARED;
    }
    final Node predecessor() throws NullPointerException {
    
    
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }

    Node() {
    
        // Used to establish initial head or SHARED marker
    }

    Node(Thread thread, Node mode) {
    
         // Used by addWaiter
        this.nextWaiter = mode;
        this.thread = thread;
    }

    Node(Thread thread, int waitStatus) {
    
     // Used by Condition
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

       The CLH queue mechanism in AQS is as follows.
Insert picture description here


3.14.4 AQS composition

       A doubly linked list, one points to the head, one points to the tail, plus a state to indicate attributes.

/**
 * Head of the wait queue, lazily initialized.  Except for
 * initialization, it is modified only via method setHead.  Note:
 * If head exists, its waitStatus is guaranteed not to be
 * CANCELLED.
 */
private transient volatile Node head;

/**
 * Tail of the wait queue, lazily initialized.  Modified only via
 * method enq to add new wait node.
 */
private transient volatile Node tail;

/**
 * The synchronization state.
 */
private volatile int state;



3.14.5 Source code method analysis

       Add a new node to the CLH queue (tail insertion method), and add a new node to the tail.

private Node enq(final Node node) {
    
    
	//循环直到尾节点不为null,添加到尾部
    for (;;) {
    
    
        Node t = tail;
        if (t == null) {
    
     // Must initialize
        	//当前head为null比较并更新成新Node,是Unsafe提供的方法,可以看出头Node不存线程。
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
    
    
            node.prev = t;
            if (compareAndSetTail(t, node)) {
    
    
                t.next = node;
                return t;
            }
        }
    }
}

       addWaiter method

private Node addWaiter(Node mode) {
    
    
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    //当尾节点不为null,直接入队列
    if (pred != null) {
    
    
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
    
    
            pred.next = node;
            return node;
        }
    }
   	//当尾节点为null,循环先构建head节点然后入队列
    enq(node);
    return node;
}

       acquireQueued method

final boolean acquireQueued(final Node node, int arg) {
    
    
    boolean failed = true;
    try {
    
    
        boolean interrupted = false;
        for (;;) {
    
    
            final Node p = node.predecessor();
            //tryAcquire(arg)抽象方法,子类具体实现
            if (p == head && tryAcquire(arg)) {
    
    
            	//成功,新的头节点
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
    
    
        if (failed)
            cancelAcquire(node);
    }
}

3.14.6 Summary

       There are still many methods that are not mentioned in AQS, which is really not moving. As the most difficult piece of Java source code, the author is also very worried about writing this point. Although I have read it many times, many details are still not there. Understand, I should first talk about AQS before writing locks, but AQS is really too difficult. It is easier to understand if you first know the locks and then look at AQS.


  1. JAVA source AQS explain ↩︎

Guess you like

Origin blog.csdn.net/weixin_44671737/article/details/114041183