AQS入队方法enq分析(发布日期:2021-02-21)

2021-02-21  本文已影响0人  watermountain

JDK版本:
1.8.0_171
方法:
java.util.concurrent.locks.AbstractQueuedSynchronizer#enq

/**
 * 将节点插入队列,必要时进行初始化。
 * Inserts node into queue, initializing if necessary. See picture above.
 * @param node the node to insert 要插入的节点
 * @return node's predecessor 前驱节点
 */
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        // t == null 等价于 tail == null
        // compareAndSetHead 更新队列头
        // tail = head 队列头、队列尾指向哑节点(dummy node)new Node()创建的节点
        /**
         * 初始化之后的队列
         * <pre>
         *          +------+ 
         * head --> |      |  <-- tail
         *          +------+ 
         * </pre>
         */
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            // 初始化之后,入队节点
            // 入队节点的前一个节点设置为tail节点
            // 
            node.prev = t; // 设置插入节点node前驱节点
            if (compareAndSetTail(t, node)) { // 将插入节点node设置为tail节点, compareAndSetTail只更新tail, t的引用(指向的对象不变)
                t.next = node; // 插入节点node的前驱节点的下一个节点为要插入的节点node
                return t;  // 返回插入节点node的前驱节点
            }
            /**
             * <pre>        
             *          +------+ <-- +------+ 
             * head --> |      |     |      |  <-- tail
             *          +------+ --> +------+ 
             *           哑巴节点      要插入的节点node
             * </pre>
             */
        }
    }
}

以给定模式(独占或共享)为当前线程创建(等待队列)节点,并将创建的节点加入等待队列

    /**
     * Creates and enqueues node for current thread and given mode.
     *
     * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
     * @return the new 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) { // 队列非空时
            /**
             * 队列非空时, 执行插入节点之前的逻辑
             * <pre>        
             *          +------+ <-- +------+ 
             * head --> |      |     |      |  <-- tail
             *          +------+ --> +------+ 
             *           哑巴节点      要插入的节点node
             * </pre>
             */
            node.prev = pred; // 插入节点node的前驱节点为尾节点
            if (compareAndSetTail(pred, node)) { // 插入节点node设置为尾节点 
                pred.next = node; // 前驱节点的下一个节点为插入节点node
                return node; // 返回插入节点node
            }
        }
        /**
         * <pre>                    
         *          +------+ <-- +------+ <-- +------+ 
         * head --> |      |     |      |     |      |  <-- tail
         *          +------+ --> +------+ --> +------+ 
         *           哑巴节点                   要插入的节点node 
         * </pre>
         */
        enq(node);
        return node;
    }

CAS 更新队列头节点、尾节点方法,仅入队时使用

/**
 * CAS head field. Used only by enq.
 */
private final boolean compareAndSetHead(Node update) {
    return unsafe.compareAndSwapObject(this, headOffset, null, update);
}

/**
 * CAS tail field. Used only by enq.
 */
private final boolean compareAndSetTail(Node expect, Node update) {
    return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}

哑节点:
可以起到避免处理头节点为空的边界问题的作用,减少代码执行异常的可能性

上一篇下一篇

猜你喜欢

热点阅读