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

2019-12-07  本文已影响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);
    }
上一篇下一篇

猜你喜欢

热点阅读