The difference between

In fact, the first case and the second case are the difference between tryAcquire method,

tryAcquire

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if(! hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true; }}else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
Copy the code

Mainly! Hasqueuedtoraise () this method distinguishes between two cases: if it is true, the attempt is made, and if false, it is not, and the code moves down

hasQueuedPredecessors

public final boolean hasQueuedPredecessors(a) {
    Node t = tail; 
    Node h = head;
    Node s;
    returnh ! = t && ((s = h.next) ==null|| s.thread ! = Thread.currentThread()); }Copy the code

! Two cases of hasqueuedToraise ()

  1. True blocks if the queue is empty, or if the queue is not empty, the next node on the head node is not null, and the thread on the next node on the head node is the same thread as the current thread
  2. False The blocking queue is not empty, or the next node of the header is null, or the thread of the next node of the header is different from the current thread

The same

acquireQueued

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;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true; }}finally {
        if(failed) cancelAcquire(node); }}Copy the code

This method also seems very simple, actually is the first time came in distinguish the head node, if the node is head attempts to acquire locks, if not go shouldParkAfterFailedAcquire method

  • Note that this method only attempts to acquire the lock if the current node’s prefix node is a header
  • It only tries when it comes in and the current node is a head node, and if it comes in and the current node prefix is not a head node, then it’s unlikely to try any of the next three times because the position of the node hasn’t changed, but there seems to be an extreme case, If a node in the blocking queue does not wait between the current node and the current node, the current node will hang directly behind the head node, and then the method will be tried again
  • In the acquireQueued method, a maximum of three attempts can be made to acquire the lock if, in extreme cases, the current node prefixes the current node to the header and the current thread is different from the header thread and three attempts fail
  • If the current node is prefixed by a header node and the current thread node has the same thread as the header thread, the tryAcquire method will return true

shouldParkAfterFailedAcquire

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}
Copy the code

This method returns true at most a few times. It can be seen from the judgment conditions of this method that the value of state should be in three cases

  1. 0
  2. – 1
  3. Greater than zero

So let’s just talk about,

  1. If it is 0, it will change to -1 and return the second time. (This doesn’t really exist, because every thread that enters the blocking queue will eventually become -1, but if no new node has entered since then, it may be 0.)
  2. If it’s minus 1, you come in and you go straight back
  3. If it’s greater than 0, then it goes through the loop and it doesn’t return until the prefix node is equal to 0 or equal to -1. If it’s equal to 0, it goes to the first case on the second entry, and generally it doesn’t return true until the third entry.

So, this method can return true at most three times.

The change of waitState

conclusion

There are two cases,

  1. When the blocking queue is empty, or the queue is not empty, the next node of the head node is not null, and the thread of the next node of the head node is the same thread as the current thread, this is a maximum of 3 attempts

The result of two attempts is that the blocking queue is empty, tryAcquire will try once, then go to addWaiter, the current node will become the head of the blocking queue with state 0, then acquireQueued, Will try twice because the state of 0 shouldParkAfterFailedAcquire will return true, then the most is to try three times

  1. The blocking queue is not empty, or the next node of the head node is null, or the thread of the next node of the head node is different from the current thread, which is a maximum of 3 attempts

The next node of the head node is not the same thread as the current thread, assuming that the lock is always contested, then finally