shouldParkAfterFailedAcquire执行流程:
1、如果前节点status为signal,直接返回。(表示后节点可放心休眠)
2、前节点为cancel状态,则跳过前节点,并关联到前面第一个不为cancel状态的节点3、前节点为propagate、conditions、0 等状态则修改为为signal状态。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
cancelAcquire函数执行流程:
1、将包含的线程删除,跳过当前节点前面已经处于canceled状态的节点
2、将当前节点状态设置为cancel
3、如果当前节点是尾节点,设置当前节点的前节点为尾节点,并且把前节点(已经成为尾节点)的next指针置为空,完成出队操作。如下图所示
4、如果当前节点不是尾节点,执行流程如下
(1)前节点不为头节点
(2)前节点的包含一个线程
(3)前节点状态为signal 或者 (前节点状态小于0,但是成功将前节点状态改为为signal)
如果满足如上条件;
执行如下操作:获取当前节点的下一个节点(下称后节点),如果后节点不为空,并且状态小于0,将当前节点移出队列,即将前节点的后节点设置为当前节点的后节点。就是说这个时候出队操作分为两步 :
a. 将node.pred的next指向node.successor;
b. 将node.successor的prev指向node.pred。
但是cancelAcquire只完成了第一步,那么第二步是在哪里做的呢。一般是在shouldParkAfterFailedAcquire()函数里完成的。判断是是否休眠的时候检查前继节点,顺便跳过前面状态为1的节点。
不满足:唤醒当前节点的后继节点。