This time to complete the hole left by the last Lock, the rest of the source code supplement.

AQS releases lock logic

release(int arg)

Release the call relationship of the lock

Reentrantlock.unlock () — > sync.release() — > Relsese provided by AQS

public void unlock(a) {
    sync.release(1);
}

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if(h ! =null&& h.waitStatus ! =0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

protected final boolean tryRelease(int releases) {
    // Subtract the freed value..
    int c = getState() - releases;
    // If the current thread does not hold the lock, the exception is direct.
    if(Thread.currentThread() ! = getExclusiveOwnerThread())throw new IllegalMonitorStateException();
    // The current thread holds the lock
    boolean free = false;// Whether the lock has been fully released
    // The lock is fully released only if the state of the current thread is 0
    if (c == 0) {
      free = true;
      setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

Copy the code

unparkSuccessor(Node node)

The unparksucceeded in AQS.

Wake up the next node of the current node.

    private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0) // Change to 0 because the current node has already finished waking up its successors.
            compareAndSetWaitStatus(node, ws, 0);
        // The first successor of the current node
        Node s = node.next;
        /* * * when s = null *1. When the current node is the tail node *2. When the node is not enqueued * restate the enqueued process: * * set the prev of the new node to pred * * set the prev of the new node to pred * * set the tail of the new node to cas * ③ pred. Then you get node.next as Null * need to find a node to wake up */
        // Let's look at condition 2
        // If you enter condition 2, then it means that s! = null
        // The s node is in the cancelled state, so you need to find a suitable node to wake up
        if (s == null || s.waitStatus > 0) {
            // Find nodes that can be awakened
            s = null;
            // If you walk through the end of the queue, you will find the node closest to the current node that can be awakened.
            // Node may not be found, node may be null
            for(Node t = tail; t ! =null&& t ! = node; t = t.prev)if (t.waitStatus <= 0)
                    s = t;
        }
        // If a suitable node can be awakened, wake up.. Do nothing until you can find it.
        if(s ! =null)
            LockSupport.unpark(s.thread);
    }
Copy the code

To summarize the above three methods. First of all, if we call ReentrantLock unlock, we’re actually calling AQS Release. So in release we’re going to first try to release the lock, and when do we fully release the lock, when the state of the current thread is equal to 0. If the current state value is 0, then we try to wake up the successor node if the current thread is not empty and not the tail node. Unblock the queue to find the nearest node and wake up the node whose waitStatus is less than or equal to 0. This is the general logic for releasing locks.

AQS response is interrupted

cancelAcquire(Node node)

The specified node is disabled from competing

private void cancelAcquire(Node node) {
    if (node == null)
      return;
        // The current thread associated with node is NULL because the queue has been unqueued
    node.thread = null;
    Node pred = node.prev;
    while (pred.waitStatus > 0)
      node.prev = pred = pred.prev;
    Node predNext = pred.next;
    // Set the current node state to cancel state 1
    node.waitStatus = Node.CANCELLED;
    /* * The dequeueing policies are different depending on the location of the node in the queue. There are three cases: * 1. 2. The current node is not a head.next node, nor is it tail * 3. The current node is the head.next node */
    // Condition 1: node == tail The current node is the tail of the team
    // Condition two: If the tail is successfully modified, the tail is successfully modified
    if (node == tail && compareAndSetTail(node, pred)) {
      // If the current node is the end of the queue, you must disconnect the node from its predecessor and then point to the predecessor
      compareAndSetNext(pred, predNext, null);
    } else {
      int ws;
      // The current node is not a head.next node, nor is it tail
      if(pred ! = head &&// Condition 2.1: True, indicating that the node precursor state is signal
          // Not true: the precursor state may be 0 or 1 (the precursor is also unqueued..)
          // Condition 2.2: If the precursor state is <= 0, set the precursor state to Signal.. Indicates to wake up a successor node.
          Pred. next -> node.next; pred = signal
          ((ws = pred.waitStatus) == Node.SIGNAL ||
           (ws <= 0&& compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread ! =null) {
        // queue out: pred.next -> node.next, when node.next is woken up
        / / call shouldParkAfterFailedAcquire makes the node. The next node across the cancel state node
        // Complete the actual exit
        Node next = node.next;
        if(next ! =null && next.waitStatus <= 0)
          compareAndSetNext(pred, predNext, next);
      } else {

        // The current node is the head.next node
        / / subsequent nodes after wake up, will call shouldParkAfterFailedAcquire makes the node. The next node across the cancel state node
        Next -> head -> next -> head -> next -> head
        unparkSuccessor(node);
      }
      // Point to yourself and get out
      node.next = node; // help GC}}Copy the code

AQS unfair lock

A few words about unfair locks, by the way, if you understand fair locks, unfair locks are only slightly different.

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    final void lock(a) {
        // Try to lock the current state, regardless of whether there are other threads waiting in the queue or whether the current state is locked.
        if (compareAndSetState(0.1))
            // Preemption succeeded in setting exclusive
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }

    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            // If the current status is 0, it still does not determine whether there are waiting threads in the current queue, and tries to grab the lock directly
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true; }}// Failed to check for reentrant
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false; }}// The following is the same, the queue to join the queue, the resource to occupy the resource
public final void acquire(int arg) {
    if(! tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }Copy the code