AbstractQueuedSynchronizer::acquire源码笔记

此处的acquire方法指AQS内部的私有acquire方法,方法签名为

final int acquire(Node node, int arg, boolean shared,
                    boolean interruptible, boolean timed, long time)
static final int WAITING   = 1;          // must be 1
static final int CANCELLED = 0x80000000; // must be negative
static final int COND      = 2;          // in a condition wait


// 此方法为私有的acquire方法,所有暴露出去的acquire方法最终都会使用不同的参数组合调用此方法
final int acquire(Node node, int arg, boolean shared,
                    boolean interruptible, boolean timed, long time) {
    Thread current = Thread.currentThread();
    byte spins = 0, postSpins = 0;   // retries upon unpark of first thread
    boolean interrupted = false, first = false;
    Node pred = null;                // predecessor of node when enqueued

    /*
        * Repeatedly:
        *  Check if node now first
        *    if so, ensure head stable, else ensure valid predecessor
        *  if node is first or not yet enqueued, try acquiring
        *  else if node not yet created, create it
        *  else if not yet enqueued, try once to enqueue
        *  else if woken from park, retry (up to postSpins times)
        *  else if WAITING status not set, set and retry
        *  else park and clear WAITING status, and check cancellation
        */

    for (;;) {
        // 3个判断条件
        // 1.此节点当前不是首节点
        // 2.此节点前驱节点不为null
        // 3.此节点前驱节点不是头结点,注意头结点跟首节点不是一个概念
        if (!first && (pred = (node == null) ? null : node.prev) != null &&
            !(first = (head == pred))) {
            // pred.status<0 说明前驱节点已经被取消,此时做一次等待队列清理
            if (pred.status < 0) {
                cleanQueue();           // predecessor cancelled
                continue;
            // 如果此节点的前驱节点的前驱节点为null,则直接跳到下一次循环
            // Thread.onSpinWait(); 可方便虚拟机优化自选等待的过程,本身不包含额外的逻辑
            } else if (pred.prev == null) {
                Thread.onSpinWait();    // ensure serialization
                continue;
            }
        }
        // 两个并列条件
        // 1.当前节点是首节点
        // 2.当前节点前驱节点未null,说明当前节点还未入队
        // 只有这里的逻辑用到了tryAcquire,说明已经入队的非首节点是不能获取锁的
        if (first || pred == null) {
            boolean acquired;
            // 基于是否为共享获取模式分别调用tryAcquireShared与tryAcquire
            // 这两个方法都是无实现方法,需要用户自己实现其语义
            try {
                if (shared)
                    acquired = (tryAcquireShared(arg) >= 0);
                else
                    acquired = tryAcquire(arg);
            } catch (Throwable ex) {
                cancelAcquire(node, interrupted, false);
                throw ex;
            }
            // acquired == true 说明获取锁成功
            if (acquired) {
                // first == true 说明当前节点为首节点
                // 需要将它出队并通知后继节点,这里采用的出队方式是将前导节点删除,将此节点数据清零作为新的头结点
                if (first) {
                    node.prev = null;
                    head = node;
                    pred.next = null;
                    node.waiter = null;
                    // 如果是共享锁,通知后继节点
                    if (shared)
                        signalNextIfShared(node);
                    if (interrupted)
                        current.interrupt();
                }
                return 1;
            }
        }
        // 走到这里 node == null 说明未获取到锁并且node尚未建立(一个新线程第一次获取锁会出现这种情况)
        // 发现这种情况,建立新的node然后重试,新建立的node还没有入队,所以其中的数据是什么无所谓,程序因此选择建立空node
        if (node == null) {                 // allocate; retry before enqueue
            if (shared)
                node = new SharedNode();
            else
                node = new ExclusiveNode();
        // 走到这里,说明node已经建立但是并未入队,且获取锁失败
        // 这种情况下,将node入队然后再次进行尝试
        } else if (pred == null) {          // try to enqueue
            node.waiter = current;
            Node t = tail;
            node.setPrevRelaxed(t);         // avoid unnecessary fence
            if (t == null)
                tryInitializeHead();
            else if (!casTail(t, node))
                node.setPrevRelaxed(null);  // back out
            else
                t.next = node;
        // 当前节点为首节点且自旋倒计时spin!=0
        // 此时说明该节点被上一个节点唤醒成为首节点,而且自旋时间还未走完
        } else if (first && spins != 0) {
            --spins;                        // reduce unfairness on rewaits
            Thread.onSpinWait();
        // 未获取到锁,且当前node状态为0(未定义)
        // 此时node有两种情况,1-第一次入队获取锁失败走到这里; 2-状态为WAITING的情况下获取锁失败被park,然后被前驱节点唤醒,唤醒之后一定为首节点,其状态被复位,然后自旋时间走完来到这里
        } else if (node.status == 0) {
            node.status = WAITING;           // enable signal and recheck
        } else {
            long nanos;
            // (postSpins << 1) | 1 等价于 postSpins*2+1
            spins = postSpins = (byte)((postSpins << 1) | 1);
            // 有时间限制的park和无时间限制的park
            if (!timed)
                LockSupport.park(this);
            else if ((nanos = time - System.nanoTime()) > 0L)
                LockSupport.parkNanos(this, nanos);
            else
                break;
            // 走到这里说明线程从park状态被唤醒
            node.clearStatus();
            if ((interrupted |= Thread.interrupted()) && interruptible)
                break;
        }
    }
    return cancelAcquire(node, interrupted, interruptible);
}

 

posted @ 2021-04-14 20:54  榆木脑袋0v0  阅读(129)  评论(0)    收藏  举报