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 ...
随机推荐
- MySQL bit类型增加索引后查询结果不正确案例浅析
昨天同事遇到的一个案例,这里简单描述一下:一个表里面有一个bit类型的字段,同事在优化相关SQL的过程中,给这个表的bit类型的字段新增了一个索引,然后测试验证 时,居然发现SQL语句执行结果跟不加索 ...
- [代码]C语言进行md5,SHA256,SHA512加密
前言 原本在学puppet,它的user资源需要设置hash后的散列值,结果-我把加密算法,shadow文件,密码破解搞了个遍- 环境 CentOS7 gcc编译器 /etc/shadow文件解析 文 ...
- TGI 基准测试
本文主要探讨 TGI 的小兄弟 - TGI 基准测试工具.它能帮助我们超越简单的吞吐量指标,对 TGI 进行更全面的性能剖析,以更好地了解如何根据实际需求对服务进行调优并按需作出最佳的权衡及决策.如果 ...
- QT学习:05 元对象系统
--- title: framework-cpp-qt-05-元对象系统 EntryName: framework-cpp-qt-05-mos date: 2020-04-09 17:11:44 ca ...
- 【论文阅读】VDBFusion: Flexible and Efficient TSDF Integration of Range Sensor Data
Type: Sensors Year: 2022 tag: Mapping 组织: Bonn 参考与前言 论文链接:https://www.ncbi.nlm.nih.gov/pmc/articles/ ...
- 【ClickHouse】0:clickhouse学习1之数据引擎(数据库引擎,表引擎)
ClickHouse提供了大量的数据引擎,分为数据库引擎.表引擎,根据数据特点及使用场景选择合适的引擎至关重要,这里根据资料做一些总结. 数据库引擎官方文档: https://clickhouse.t ...
- Mysql生成实体类
-- 查询数据表结构 SELECT CONCAT('"e.',SUBSTRING(COLUMN_NAME,1),',"+'),COLUMN_NAME,',',COLUMN_TYPE ...
- 树莓派4B-GPIO控制步进电机
树莓派4B-GPIO控制步进电机 硬件需求: 步进电机 树莓派 杜邦线 L298N驱动模块 选择步进电机 首先需要确认步进电机,因为步进电机可分为单极性和双极步进电动机两种,这两种电机的驱动方式是不同 ...
- 类、事件与对象---Dad&Mom简单练习
目的: 模拟一个家庭日常发生的场景:妈妈做好饭,说:"开饭了!",这是爸爸听到了妈妈的喊话就立马动身开始饭吃.而儿子此时正在打游戏,于是他就说:"等我打完这把游戏再吃!& ...
- redis-sort by
对某个列表(list).集合(set).有序集合(zset)排序的时候按照某个 参考键 进行排序,而不是按照按照这个列表.集合或有序集合本身进行排序: 被排序的键和参考键在业务上有关联(这个由业务保证 ...