AQS thread status:


  /** 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;
Copy the code

1. Acquire function analysis

If tryAcquire fails, execute addWaiter(Node.EXCLUSIVE) and acquireQueued(Node, ARg). If tryAcquire fails, execute addWaiter(Node.EXCLUSIVE) and acquireQueued(Node, ARg). If acquireQueued returns true, the current thread is interruptible. SelfInterrupt () is executed to set the current thread to interruptible.

public final void acquire(int arg) {
        if(! tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }static void selfInterrupt(a) {
        Thread.currentThread().interrupt();
    }
Copy the code

2. TryAcquire function analysis

protected boolean tryAcquire(int unused) {
            // If the update succeeds, set thread exclusive and return true
            if (compareAndSetState(0.1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
Copy the code

AddWaiter (node.exclusive

Create a node in EXCLUSIVE mode. 2. Use the current tail node as a pred; If pred is not empty, there is a tail node. Set the front node of the current node to pred, and CAS sets the current node to the new tail node. Then next points to the node and returns the new tail node. 4. If pred is empty, end(node);

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;
        if(pred ! =null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
Copy the code

If tail is empty, CAS creates an empty Head node. And set it to tail; 6. If tail is not empty, set prev of the current node to the tail node, CAS sets node of the current node to the new tail node, next of the old tail node to the node, and return the new tail node.

private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    returnt; }}}}Copy the code

AcquireQueued (Node, ARG) function analysis

1. Define two flags to mark whether to cancel lock grab and whether to mark interrupt respectively; This code loops indefinitely until the thread acquires the lock or cancels the lock. 3. If the front node is the head node, there is no node in front of the current node waiting for the lock resource. If the snatch is successful, the current node is set to head node, the next node of the front node P is set to null for GC collection, and the flag of the snatch cancellation is set to false. The finally code is not executed, and the interrupt flag=false is returned. 4, if p is not the head front node, or rob lock failure, is performed: shouldParkAfterFailedAcquire (p, node) && parkAndCheckInterrupt ();

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // node p is not the head node, or lock capture failed, execute here, suspend the current thread
                if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                    interrupted = true; }}finally {
            // If the suspension is abnormal, cancel the lock snatch operation
            if(failed) cancelAcquire(node); }}Copy the code

Get the status of the front node. If the status is SIGNAL(SIGNAL means that the next node will wake up immediately after the lock is released), return true. 6. If ws>0, the status of the current node is CANCELLED, find the node pred that is not CANCELLED, set the next node of the pred node to the current node, and return false. 7, if ws<=0, set preD to SIGNAL and return false;

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /* * This node has already set status asking a release * to signal it, so it can safely park. */
            return true;
        if (ws > 0) {
            /* * Predecessor was cancelled. Skip over predecessors and * indicate retry. */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            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. */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
Copy the code

Suspend the current thread, return interrupt flag true;

private final boolean parkAndCheckInterrupt(a) {
        LockSupport.park(this);
        return Thread.interrupted();
    }
Copy the code

CancelAcquire (Node

1. If node is empty, return; 2. If node is not null, set the thread of node to null. 3. Obtain the pred of the current node, and judge the state of the pred node. If the state is greater than 0(canceled state), then loop forward to find the node that is not canceled, and set it as the new node. 4. Obtain the next node predNext and set the current node status to CANCELLED; 5. If the current node is a tail node, CAS sets pred node to the new tail node. If the operation succeeds, next node of pred is set to null. 6. If the node is not a tail node or the CAS fails to set tail, run the following command: 6.1. If pred is not head, the state of the preD is SIGNAL, and the thread is not empty, then get the next node of the node. If the next node is not empty, and the state is less than 0(not canceled), then set the next node of pred to the next node of the node. 6.2 If the pred node is the head node, or the state is not SIGNAL, or the thread is empty, the unparksucceeded function is called to wake up the succeeding threads to prevent the queue from failing;

private void cancelAcquire(Node node) {
        // Ignore if node doesn't exist
        if (node == null)
            return;

        node.thread = null;

        // Skip cancelled predecessors
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        // predNext is the apparent node to unsplice. CASes below will
        // fail if not, in which case, we lost race vs another cancel
        // or signal, so no further action is necessary.
        Node predNext = pred.next;

        // Can use unconditional write instead of CAS here.
        // After this atomic step, other Nodes can skip past us.
        // Before, we are free of interference from other threads.
        node.waitStatus = Node.CANCELLED;

        // If we are the tail, remove ourselves.
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } else {
            // If successor needs signal, try to set pred's next-link
            // so it will get one. Otherwise wake it up to propagate.
            int ws;
            if(pred ! = head && ( (ws = pred.waitStatus) == Node.SIGNAL || (ws <=0&& compareAndSetWaitStatus(pred, ws, Node.SIGNAL)) ) && pred.thread ! =null
               ) {
                Node next = node.next;
                if(next ! =null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
                unparkSuccessor(node);
            }

            node.next = node; // help GC}}Copy the code