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方法解读的更多相关文章

  1. 并发编程(十二)—— Java 线程池 实现原理与源码深度解析 之 submit 方法 (二)

    在上一篇<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法.这篇文章是接着上一篇文章 ...

  2. spring5 源码深度解析----- AOP目标方法和增强方法的执行(100%理解AOP)

    上一篇博文中我们讲了代理类的生成,这一篇主要讲解剩下的部分,当代理类调用时,目标方法和代理方法是如何执行的,我们还是接着上篇的ReflectiveMethodInvocation类Proceed方法来 ...

  3. 源码深度解析SpringMvc请求运行机制(转)

    源码深度解析SpringMvc请求运行机制 本文依赖的是springmvc4.0.5.RELEASE,通过源码深度解析了解springMvc的请求运行机制.通过源码我们可以知道从客户端发送一个URL请 ...

  4. SpringMVC 源码深度解析&lt;context:component-scan&gt;(扫描和注冊的注解Bean)

    我们在SpringMVC开发项目中,有的用注解和XML配置Bean,这两种都各有自己的优势,数据源配置比較经经常使用XML配置.控制层依赖的service比較经经常使用注解等(在部署时比較不会改变的) ...

  5. mybatis 3.x源码深度解析与最佳实践(最完整原创)

    mybatis 3.x源码深度解析与最佳实践 1 环境准备 1.1 mybatis介绍以及框架源码的学习目标 1.2 本系列源码解析的方式 1.3 环境搭建 1.4 从Hello World开始 2 ...

  6. 并发编程(十五)——定时器 ScheduledThreadPoolExecutor 实现原理与源码深度解析

    在上一篇线程池的文章<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中从ThreadPoolExecutor源码分析了其运行机制.限于篇幅,留下了Scheduled ...

  7. spring源码深度解析— IOC 之 容器的基本实现

    概述 上一篇我们搭建完Spring源码阅读环境,spring源码深度解析—Spring的整体架构和环境搭建 这篇我们开始真正的阅读Spring的源码,分析spring的源码之前我们先来简单回顾下spr ...

  8. spring源码深度解析— IOC 之 默认标签解析(上)

    概述 接前两篇文章  spring源码深度解析—Spring的整体架构和环境搭建  和  spring源码深度解析— IOC 之 容器的基本实现 本文主要研究Spring标签的解析,Spring的标签 ...

  9. spring源码深度解析— IOC 之 默认标签解析(下)

    在spring源码深度解析— IOC 之 默认标签解析(上)中我们已经完成了从xml配置文件到BeanDefinition的转换,转换后的实例是GenericBeanDefinition的实例.本文主 ...

  10. spring源码深度解析— IOC 之 开启 bean 的加载

    概述 前面我们已经分析了spring对于xml配置文件的解析,将分析的信息组装成 BeanDefinition,并将其保存注册到相应的 BeanDefinitionRegistry 中.至此,Spri ...

随机推荐

  1. WPF/C#:如何将数据分组显示

    WPF Samples中的示例 在WPF Samples中有一个关于Grouping的Demo. 该Demo结构如下: MainWindow.xaml如下: <Window x:Class=&q ...

  2. c# 基础小知识备忘录

    记性不大好,记下来 First 和 FirstOrDefault  First方法:返回List集合序列中的第一个符合条件的元素,如果没有查找到,则抛出运行时异常. FirstOrDefault方法: ...

  3. python 判断token是否有效,若失效,重新发起token请求

    场景: 1.对一个接口,进行接口自动化测试,查找的是有权限操作的用例,传入到获取token接口,生成token,判断当前是否有token,如果存在token,则无需再次发起token接口: 存在的问题 ...

  4. SpringBoot 3.x 结合 Swagger3 (Knife4j )踩坑实录

    SpringBoot 3.x + Swagger3 踩坑实录 我的是springboot 版本是:3.2.2 <parent> <groupId>org.springframe ...

  5. 09-CentOS软件包管理

    简介 CentOS7使用rpm和yum来管理软件包. CentOS 8附带YUM包管理器v4.0.4版本,该版本现在使用DNF (Dandified YUM)技术作为后端.DNF是新一代的YUM,新的 ...

  6. 在 VSCode 中编写 Markdown 的进阶指南

    最新版的 Visual Studio Code 对 Markdown 的支持已显著提升,其在预览方面的体验甚至可以与 Markdown Preview Enhanced 插件相比.本文将介绍一些优化方 ...

  7. 聊一聊 C# 弱引用 底层是怎么玩的

    一:背景 1. 讲故事 最近在分析dump时,发现有程序的卡死和WeakReference有关,在以前只知道怎么用,但不清楚底层逻辑走向是什么样的,借着这个dump的契机来简单研究下. 二:弱引用的玩 ...

  8. 3568F-物联网模块开发案例

  9. SSM配置文件分类及总结

    配置组件通常涉及以下几个方面 数据访问配置 配置数据源.JdbcTemplate.事务管理器等,以支持数据库操作. 服务层与DAO层配置 定义服务类和服务实现类.数据访问对象(DAO)的bean,以及 ...

  10. Dotnet算法与数据结构:Hashset, List对比

    哈希集A 是存储唯一元素的集合.它通过在内部使用哈希表来实现这一点,该哈希表为基本操作(如添加.删除和包含)提供恒定时间平均复杂度 (O(1)).此外,不允许重复元素,使其成为唯一性至关重要的场景的理 ...