美文网首页
探索AQS等待队列-哨兵节点

探索AQS等待队列-哨兵节点

作者: 隐藏的记号 | 来源:发表于2019-12-07 23:59 被阅读0次

    Question:AQS的等待队列初始化后,Head节点会一直是哨兵节点吗?

    why is the question:

    在AQS中,线程竞争锁失败后将被放入AQS实例的等待队列中。

    private Node enq(final Node node) {
            for (;;) {
                Node t = tail;
                if (t == null) { // Must initialize
                    if (compareAndSetHead(new Node()))
                        tail = head;
                } else {
                    node.prev = t;
                    if (compareAndSetTail(t, node)) {
                        t.next = node;
                        return t;
                    }
                }
            }
        }
    
    image.png image.png

    第一次初始化等待队列,队列中队头为哨兵节点。而后,每次真正的线程节点都是拼接在尾部。

    比如,在食堂等待打饭菜的队伍中,同学A开始找到了队尾开始入队,排在A前面的是同学B。当前关系是,A的前面是B,队尾不再是B,而是新入队的A。如果要满足双向链表定义,则B的后面是A。

    当多个线程竞争同一个锁失败后,都进入了等待队列,队列的情况大概是这样的:

    哨兵节点 ← node-1 ←  node-2 ← node-3 
    

    tail节点是Node · 3,如果这时候当前持有锁线程释放锁,假设按照公平锁策略,node-1将要获取锁,可是我看到AQS中释放锁代码部分却是:

    public final boolean release(int arg) {
            if (tryRelease(arg)) {
                Node h = head;
                if (h != null && h.waitStatus != 0)
                    unparkSuccessor(h);  // 将头部节点传入了unpark方法
                return true;
            }
            return false;
        }
    

    释放锁的操作,选定了 head 节点作为下一个线程激活对象,可这head可不是哨兵节点嘛?哨兵节点里面并没有其代表的线程,源码中这么写是为什么?

    try geting answer:

    在前面写问题过程时,就突然想到了,head节点虽然哨兵节点,但是 head.next不就是等待队列中最早等待的那个线程吗?

    点开unparkSuccessor(h),这个方法里面激活的还真的是head.next,说明前面的问题其实是肯定的,head节点,在队列初始化之后,一直都是哨兵节点。

    private void unparkSuccessor(Node node) {
            /*
             * If status is negative (i.e., possibly needing signal) try
             * to clear in anticipation of signalling.  It is OK if this
             * fails or if status is changed by waiting thread.
             */
            int ws = node.waitStatus;
            if (ws < 0)
                compareAndSetWaitStatus(node, ws, 0);
    
            /*
             * Thread to unpark is held in successor, which is normally
             * just the next node.  But if cancelled or apparently null,
             * traverse backwards from tail to find the actual
             * non-cancelled successor.
             */
            // 实际激活的线程节点
            Node s = node.next;
            if (s == null || s.waitStatus > 0) {
                s = null;
                for (Node t = tail; t != null && t != node; t = t.prev)
                    if (t.waitStatus <= 0)
                        s = t;
            }
            if (s != null)
                LockSupport.unpark(s.thread);
        }
    

    相关文章

      网友评论

          本文标题:探索AQS等待队列-哨兵节点

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