AQS源码深度解析之cancelAcquire方法解读
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指针开始从后往前,找到最靠近头的有效(非取消)节点,唤醒这个线程。
完美!
AQS源码深度解析之cancelAcquire方法解读的更多相关文章
- 并发编程(十二)—— Java 线程池 实现原理与源码深度解析 之 submit 方法 (二)
在上一篇<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法.这篇文章是接着上一篇文章 ...
- spring5 源码深度解析----- AOP目标方法和增强方法的执行(100%理解AOP)
上一篇博文中我们讲了代理类的生成,这一篇主要讲解剩下的部分,当代理类调用时,目标方法和代理方法是如何执行的,我们还是接着上篇的ReflectiveMethodInvocation类Proceed方法来 ...
- 源码深度解析SpringMvc请求运行机制(转)
源码深度解析SpringMvc请求运行机制 本文依赖的是springmvc4.0.5.RELEASE,通过源码深度解析了解springMvc的请求运行机制.通过源码我们可以知道从客户端发送一个URL请 ...
- SpringMVC 源码深度解析<context:component-scan>(扫描和注冊的注解Bean)
我们在SpringMVC开发项目中,有的用注解和XML配置Bean,这两种都各有自己的优势,数据源配置比較经经常使用XML配置.控制层依赖的service比較经经常使用注解等(在部署时比較不会改变的) ...
- mybatis 3.x源码深度解析与最佳实践(最完整原创)
mybatis 3.x源码深度解析与最佳实践 1 环境准备 1.1 mybatis介绍以及框架源码的学习目标 1.2 本系列源码解析的方式 1.3 环境搭建 1.4 从Hello World开始 2 ...
- 并发编程(十五)——定时器 ScheduledThreadPoolExecutor 实现原理与源码深度解析
在上一篇线程池的文章<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中从ThreadPoolExecutor源码分析了其运行机制.限于篇幅,留下了Scheduled ...
- spring源码深度解析— IOC 之 容器的基本实现
概述 上一篇我们搭建完Spring源码阅读环境,spring源码深度解析—Spring的整体架构和环境搭建 这篇我们开始真正的阅读Spring的源码,分析spring的源码之前我们先来简单回顾下spr ...
- spring源码深度解析— IOC 之 默认标签解析(上)
概述 接前两篇文章 spring源码深度解析—Spring的整体架构和环境搭建 和 spring源码深度解析— IOC 之 容器的基本实现 本文主要研究Spring标签的解析,Spring的标签 ...
- spring源码深度解析— IOC 之 默认标签解析(下)
在spring源码深度解析— IOC 之 默认标签解析(上)中我们已经完成了从xml配置文件到BeanDefinition的转换,转换后的实例是GenericBeanDefinition的实例.本文主 ...
- spring源码深度解析— IOC 之 开启 bean 的加载
概述 前面我们已经分析了spring对于xml配置文件的解析,将分析的信息组装成 BeanDefinition,并将其保存注册到相应的 BeanDefinitionRegistry 中.至此,Spri ...
随机推荐
- flutter+Springboot的结合
我们团队的开发 前端采用flutter 后端采用spring boot 首先 完成了app的图标名字的修改 在app/src/main/res/mipmap 目录中 存放app图标 图片 在Andro ...
- Springcloud开发之OpenFeign调用和认证
SpringCloud开发cloud具有巨大的灵活性. 在调用其它服务的时候有多种方式,虽然本质一样,但是细节还是有所差异. 一.概述 当a服务调用b服务的时候有多种方式进行: 1.通过openFei ...
- openfoam 修改 src 库经验记录
遇到一个问题,要把 sprayFoam 求解器的蒸发模型修改为自定义蒸发模型. sprayFoam 求解器本身没有实现蒸发模型,而是调用 $FOAM_SRC/lagrangian/intermedia ...
- selenium验证码识别,超级鹰干超级鹰
from selenium.webdriver import Edge from selenium.webdriver.common.by import By # 在这里导入浏览器设置相关的类 fro ...
- css 选择器优先级?
!important > 行内样式(比重1000)> ID 选择器(比重100) > 类选择器(比重10) > 标签(比重1) > 通配符 > 继承 > 浏览 ...
- mac idea快捷键整理
抽取局部变量 option+commamd+v 生成方法内变量 option+commamd+f 生成类的静态变量 找方法 2次shift 优化import Ctrl + Alt + O 格式化代码 ...
- GitHub 创始人资助的开源浏览器「GitHub 热点速览」
你是否注意到,现在主流的浏览器如 Chrome.Edge.Brave 和 Opera 都采用了谷歌的 Chromium 引擎?同时,谷歌每年不惜花费数十亿美元,确保其搜索引擎在 Safari 中的默认 ...
- log4cpp的安装及使用
目录 前言 安装 使用 示例代码 配置文件 编译链接 输出 前言 本文的操作均在ubuntu20.04下进行 安装 本文仅介绍从源码编译安装log4cpp的过程. ①在开始编译前,首先要确保系统中安装 ...
- [oeasy]python0027_整合程序_延迟输出时间_整合两个py程序
整合程序 回忆上次内容 通过搜索发现 time中有函数可以延迟 time.sleep(1) 还可以让程序无限循环 while True: 现在需要两个程序的整合 循环延迟输出 时间输出 编辑 ...
- oeasy教您玩转vim - 55 - # 参数替换
[Github地址] (https://github.com/overmind1980/oeasyvim) [Gitee地址] (overmind1980/oeasyvim) [蓝桥实验楼 邀请码 ...