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. k8s使用rbd作为存储

    k8s使用rbd作为存储 如果需要使用rbd作为后端存储的话,需要先安装ceph-common 1. ceph集群创建rbd 需要提前在ceph集群上创建pool,然后创建image [root@ce ...

  2. nginx 信号量

    nginx支持的信号量 TERM, INT fast shutdown QUIT graceful shutdown HUP changing configuration, keeping up wi ...

  3. Markdown常用语法详解

    背景知识 什么是html html是一种网页标记语言.我们平常见到的那么好看的网页就是通过html语言来编写的. html语言举例: <h1>hello world</h1> ...

  4. LuBase 低代码开发框架介绍 - 可私有化部署

    框架定位 面向开发人员,针对管理软件领域,对页面交互和通用功能进行高阶封装,逐步打造成平台型.生态型开发工具. 涓涓细流 ,汇聚成海,基于 PBC(组件式开发)开发理念,让功能模块的复用更简单. 让管 ...

  5. 多Github账号指定ssh-key提交代码

    问题 目前github中代码提交只能通过ssh方式. 每个github账号只能添加一个专用的ssh-key. 如果同时有多个Github账号在用的话就必须给每个账号绑定不同的ssh-key. 方法一: ...

  6. SpringBoot动态数据源配置

    SpringBoot动态数据源配置 序:数据源动态切换流程图如下: 1:pom.xml文件依赖声明 <dependency>   <groupId>org.springfram ...

  7. 基于TI Sitara系列AM5728工业开发板——FPGA视频开发案例分享

    前 言 3 1 cameralink_display案例 4 1.1 案例功能 4 1.2 操作说明 4 1.3 关键代码(MicroBlaze) 11 1.4 Vivado工程说明 16 1.5 模 ...

  8. UWP WinUI 制作一个路径矢量图标按钮样式入门

    本文将告诉大家如何在 UWP 或 WinUI3 或 UNO 里,如何制作一个路径按钮.路径按钮就是使用几何路径轮廓表示内容的按钮,常见于各种图标按钮,或 svg 系贴图矢量图按钮 在网上有非常多矢量图 ...

  9. Class 与 Style 如何动态绑定?

    Class 可以通过对象语法和数组语法进行动态绑定: 对象语法: <div v-bind:class="{ active: isActive, 'text-danger': hasEr ...

  10. Aspose 导出Excel时 隐藏指定列

    Worksheet ws = wb.Worksheets[0]; ws.Cells.HideColumn(0); //隐藏Excel第一列