/**
* 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;
}
}
}