1.背景
2.源码解读
调用该方法的地方
方法源码解读
/** * 取消获取资源(异常处理时都需要用到) * 方法主要功能: * 1.处理当前取消节点的状态; * 2.将当前取消节点的前置非取消节点和后置非取消节点"链接"起来; * 3.如果前置节点释放了锁,那么当前取消节点承担起后续节点的唤醒职责。 * * @param node */ private void cancelAcquire(Node node) { // 如果节点为空直接返回 if (node == null) return; // 处理当前取消节点的状态; node.thread = null; node.waitStatus = Node.CANCELLED; /* *这段代码用来找到前置的非取消节点 */ Node pred = node.prev; while (pred.waitStatus > 0) { // pred.waitStatus > 0 说明当前节点的前置节点已取消,应该继续向前找 pred = pred.prev; node.prev = pred; } /* *这里注意一下: * 1.pred是一个实际有效的前节点,即前置的非取消节点 * 2.pred.next 并不一定是 node节点,因为 while (pred.waitStatus > 0)前节点会向前移动 */ Node predNext = pred.next; // 修改尾指针:compareAndSetTail(node, pred),指向前置的第一个非取消节点; if (node == tail && compareAndSetTail(node, pred)) { // 将新的尾节点的next指针置空:compareAndSetNext(pred, predNext, null); // 为什么呢? 因为node==tail,说明node节点的前一个非取消节点就是就会说最后一个节点,即无下一个节点 compareAndSetNext(pred, predNext, null); } else { int ws; // compareAndSetWaitStatus(pred, ws, Node.SIGNAL) 把前置节点设置为下一个获取锁的节点 /* * 这个if的作用是:让pred节点作为下一个可以获取锁的节点 * 1.pred != head && pred.thread != null * 2. pred.waitStatus=-1 获取 compareAndSetWaitStatus(pred, ws, Node.SIGNAL */ if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; if (next != null && next.waitStatus <= 0) // 设置前置节点的下一个节点为:当前节点的下一个节点 compareAndSetNext(pred, predNext, next); } else { // 到这里说明 pred是头结点,唤醒node的下一个节点 unparkSuccessor(node); } node.next = node; // help GC } }
重点逻辑图解说明
while (pred.waitStatus > 0) node.prev = pred = pred.prev;
假设当前队列如下图:
当前节点为 t4前一个节点t3的waitStatus=1>0,会继续向前找,最后会找到t2为非取消的前置节点,循环结束
如果当节点是尾节点
/* *这里注意一下: * 1.pred是一个实际有效的前节点,即前置的非取消节点 * 2.pred.next 并不一定是 node节点,因为 while (pred.waitStatus > 0)前节点会向前移动 */ Node predNext = pred.next; // 修改尾指针:compareAndSetTail(node, pred),指向前置的第一个非取消节点; if (node == tail && compareAndSetTail(node, pred)) { // 将新的尾节点的next指针置空:compareAndSetNext(pred, predNext, null); // 为什么呢? 因为node==tail,说明node节点的前一个非取消节点就是就会说最后一个节点,即无下一个节点 compareAndSetNext(pred, predNext, null); // 方法执行结束 }
代码执行队列图:
当前节点不是尾节点的情况
int ws; // compareAndSetWaitStatus(pred, ws, Node.SIGNAL) 把前置节点设置为下一个获取锁的节点 /* * 这个if的作用是:让pred节点作为下一个可以获取锁的节点 * 1.pred != head && pred.thread != null * 2. pred.waitStatus=-1 获取 compareAndSetWaitStatus(pred, ws, Node.SIGNAL */ if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; if (next != null && next.waitStatus <= 0) // 设置前置节点的下一个节点为:当前节点的下一个节点 compareAndSetNext(pred, predNext, next); } else { // 到这里说明 pred是头结点,唤醒node的下一个节点 unparkSuccessor(node); } node.next = node; // help GC
执行示意图:
如果待取消的是t1节点,且t2节点是取消节点,则会直接执行unparkSuccessor(node);,这个过程会从tail指针开始从后往前,找到最靠近头的有效(非取消)节点,唤醒这个线程。