AQS queue synchronizer

Let’s look at a couple of states

/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;

// Remove from queue
static final int CANCELLED =  1;
// The next node is waiting to wake up,SINGAL is suspended
static final int SIGNAL    = -1;
// Conditional wake up
static final int CONDITION = -2;
/** * the waitStatus value indicating that the next acquireShared should be propagated unconditionally
static final int PROPAGATE = -3;
Copy the code

1. Immediately acquire the mode tryAcquire

protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }
Copy the code

You have to override this method, the template pattern

1. 2. Smart water bottle

/** * get in exclusive mode, ignore interrupts. Implement * by calling {at least once@link#tryAcquire}, * Reward for success. Otherwise the thread may queue * repeatedly blocking and unblocking, calling {@link*# strive to achieve} until success. You can use this method * to implement methods {@linkLock# Lock}. * *@paramArg gets parameters. This value is passed to *{@link#tryAcquire} but otherwise unexplained, and * can stand for whatever you like. * /
public final void acquire(int arg) {
        if(! tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }Copy the code

Acquire

1. First tryAcquire(ARG);

If tryAcquire() is true, the if judgment jumps out, and if tryAcquire() is false, the logic continues

2.addWaiter();

Join the team quickly from the rear node

static final Node EXCLUSIVE = null;
/** * creates and queues nodes for the current thread and the given pattern. * *@paramMode node. Exclusive is the exclusive node. Share used to share *@ returns a new node */
private Node addWaiter(Node mode) {
	/** The flag used to indicate that the node is waiting in exclusive 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

Question about this step?

In the case of concurrency, if the tail node points to two nodes at the same time, one of them will inevitably join the queue due to the failure of CMPXCHG operation

if(pred ! =null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                returnnode; }}Copy the code

Tail quick team entry failed, enter the full team entry method

Repeated calls may have performance problems. Join the team quickly before joining the team completely

// Encapsulate as a Node, loop CMPXCHG operations, and inevitably end up in a queue
 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(addWaiter(Node.EXCLUSIVE), arg))

Acquire will judge the state of the node and determine whether it needs to wake up or suspend.

acquireQueued

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                // Get the front node
                final Node p = node.predecessor();
                // If the leading node is head, try to get the lock
                if (p == head && tryAcquire(arg)) {
                    // Set yourself as the head node
                    setHead(node);
                    //help GC
                    p.next = null; // help GC
                    // There is no failure
                    failed = false;
                    return interrupted;
                }
                //p is not the head node
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true; }}finally {
            if(failed) cancelAcquire(node); }}Copy the code

shouldParkAfterFailedAcquire

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. */
             
             //if (true && parkAndCheckInterrupt())
            return true;
        if (ws > 0) {
            /* * Predecessor was cancelled. Skip over predecessors and * indicate retry. */
            do {
                //>0 is the state of Cancel
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
            
            //if (false && parkAndCheckInterrupt())
        } 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. */
             // The state of the front node needs to be handled
             // Change the state of -2-3 to -1
             // Change to SINGAL and return true
             
             //if (false && parkAndCheckInterrupt())
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
Copy the code

parkAndCheckInterrupt

Call the support primitive to suspend the thread

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

Release

release

public final boolean release(int arg) {
			// No tryRelease will throw an exception
        if (tryRelease(arg)) {
            Node h = head;
            if(h ! =null&& h.waitStatus ! =0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
Copy the code

Find the node of the first Singal from behind and wake it up

private void unparkSuccessor(Node node) {
        /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */
        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. */
        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;
        }
        if(s ! =null)
            LockSupport.unpark(s.thread);
    }
Copy the code

The header stays there waiting for the lock

The block stays hereThe thread stays in this state when suspending park