美文网首页
AQS入队方法enq分析(发布日期:2021-02-21)

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

作者: watermountain | 来源:发表于2021-02-21 08:58 被阅读0次

    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);
    }
    

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

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/zeiffltx.html