AbstractQueuedSynchronizer::cleanQueue源码笔记

/**
* Possibly repeatedly traverses from tail, unsplicing cancelled
* nodes until none are found. Unparks nodes that may have been
* relinked to be next eligible acquirer.
*/
private void cleanQueue() {
    for (;;) {                               // restart point
        for (Node q = tail, s = null, p, n;;) { // (p, q, s) triples
            // 2个并列条件
            // 1.q为null 说明当前等待队列还未初始化
            // 2.q不为null,q的前驱p为null。说明当前q是队列中的第一个节点,而等待队列的第一个节点是一个假的头结点
            // 1、2满足一个就说明当前等待队列遍历完成
            if (q == null || (p = q.prev) == null)
                return;                      // end of list
            // 以下判断为真说明q不是s的前驱节点,此时程序不知道遍历到了那里,不知道p、q、s代表的节点是否还在等待队列中,因此重新开始遍历
            if (s == null ? tail != q : (s.prev != q || s.status < 0))
                break;                       // inconsistent
            // q.status<0 说明q是一个被取消的节点
            if (q.status < 0) {              // cancelled
                if ((s == null ? casTail(q, p) : s.casPrev(q, p)) &&
                    q.prev == p) {
                    p.casNext(q, s);         // OK if fails
                    // p.prev==null 说明当前p是首节点(虚拟的头结点)
                    if (p.prev == null)
                        signalNext(p);
                }
                break;
            }
            // 上面的代码段在并发的情况下可能出现一种情况:
            // 线程1完成了s.casPrev(q, p)操作,现在s的prev指针指向了p,但是此时p的next指针依然指向q
            // 此时线程1被cpu挂起,线程2执行,当线程2执行到s时,发现p的next指针还没有改变其指向,这种情况下,由于不确定线程1何时才会再次被调度,由发现此现象的线程帮助改变p的next指针,此即为下面这段代码的目的
            if ((n = p.next) != q) {         // help finish
                if (n != null && q.prev == p) {
                    p.casNext(n, q);
                    if (p.prev == null)
                        signalNext(p);
                }
                break;
            }
            // q、s前进
            s = q;
            q = q.prev;
        }
    }
}

 

posted @ 2021-03-31 15:43  榆木脑袋0v0  阅读(121)  评论(1)    收藏  举报