JDK17 AbstractQueuedSynchronizer 二 条件队列
条件队列
同步队列中的线程是为了争抢锁,而条件队列中的线程是主动释放锁,挂起自己,等条件满足时被别的线程唤醒,继续工作。

AQS里只有1个同步队列,但可以有多个等待队列,每个等待队列对应一个ConditionObject对象。
public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();Condition con1 = lock.newCondition();Condition con2 = lock.newCondition();
}
// ReentrantLock.java
final ConditionObject newCondition() {return new ConditionObject();
}
同步队列的头尾节点是head和tail,等待队列的头尾节点是firstWaiter和lastWaiter。
同步队列的头结点是哑节点,等待队列没有哑结点。
线程想进入同步队列是没有条件的,线程想进入等待队列得先获取锁,而且还得是独占锁。
同步队列的节点和等待队列的节点都是Node对象,线程离开等待队列后会进入同步队列,继续获取锁。
等待 释放锁 await()
public final void await() throws InterruptedException {if (Thread.interrupted()) // 如果当前线程被设置了中断位,则1. 清除中断位,2. 抛出异常交给用户处理throw new InterruptedException();ConditionNode node = new ConditionNode(); // ConditionNode继承自Node类int savedState = enableWait(node); // 当前线程释放锁,并且组装node对象LockSupport.setCurrentBlocker(this); // for back-compatibility 向后兼容 与LockSupport.park()组合使用,让当前线程抛弃锁boolean interrupted = false, cancelled = false, rejected = false;while (!canReacquire(node)) { // canReacquire表示当前节点是否离开等待对了,到了同步队列。如果是,则跳出循环if (interrupted |= Thread.interrupted()) { // 响应中断if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0) // 详见下文单独介绍这个语句,含义是如果被设置中断,并且当前节点仍然在等待队列,那么离开等待队列响应中断break; // else interrupted after signal} else if ((node.status & COND) != 0) { // 当前节点的status仍包含`COND`,即仍处于等待// 这个方法体目的是阻塞当前线程,如果能交给线程池提高并发更好,如果不能交给线程池则自己执行`LockSupport.park()`,自己挂起try {if (rejected)node.block();elseForkJoinPool.managedBlock(node);} catch (RejectedExecutionException ex) {rejected = true;} catch (InterruptedException ie) {interrupted = true;}} else // 代表上一个条件为false,即当前节点的status不包含`COND`,即已经离开等待队列。但是`canReacquire(node)=false`表示当前节点还未进入同步队列。因此放弃时间片等别的线程先执行。Thread.onSpinWait(); // awoke while enqueuing}// 运行到此,当前线程被唤醒,并且在同步队列里LockSupport.setCurrentBlocker(null);node.clearStatus(); // 原子性 node.status = 0acquire(node, savedState, false, false, false, 0L); // 尝试获取锁// 运行到此,当前线程已经重新获取资源if (interrupted) { // 响应中断if (cancelled) { // 当前节点从等待队列被取消,没有进入同步队列unlinkCancelledWaiters(node); // 清理当前节点和被设置为`CANCALLED`的节点throw new InterruptedException();}Thread.currentThread().interrupt();}}
private int enableWait(ConditionNode node) {if (isHeldExclusively()) { // 判断当前线程是否是持锁线程node.waiter = Thread.currentThread();node.setStatusRelaxed(COND | WAITING); // COND = 2,WAITING = 1, 因此node的status=3ConditionNode last = lastWaiter; // 开始将当前node对象放入条件队列if (last == null)firstWaiter = node;elselast.nextWaiter = node;lastWaiter = node; // 结束将当前node对象放入条件队列int savedState = getState(); // 获取当前线程的stateif (release(savedState)) // 释放锁,并且唤醒同步队列的下一个节点return savedState;}// 如果运行到此,说明1. 锁空闲,无持锁线程 或者2. 当前线程不是持锁线程,因此取消当前节点并且抛出异常node.status = CANCELLED; // lock not held or inconsistentthrow new IllegalMonitorStateException();
}
(node.getAndUnsetStatus(COND) & COND) != 0
这个语句的目的是线程安全地将等待节点移出等待队列,即避免多个线程重复将同一个节点移出等待队列。线程如何知道节点在不在等待队列?通过status。如果status包含COND,表示还在等待队列,否则不在。
node.getAndUnsetstatus(COND)是node.status = node.status & ~COND,含义是解除COND位。原理是COND=2,用二进制表示是0010(从右往左第二位是1),与非运算之后去掉status第二位的1。

(node.getAndUnsetStatus(COND) & COND) != 0=true表示当前节点仍然在等待队列。node.getAndUnsetstatus(COND)解除COND位之后返回的是初始status。node.getAndUnsetStatus(COND) & COND是初始状态与COND进行与运算,如果初始状态含COND,那么结果不等于0,含义是当前线程运行时当前节点仍然在等待队列。否则当前节点不在等待队列。
为什么不直接判断COND位,而是原子性操作呢?是为了线程安全。对于某个等待节点,多线程环境下只有第一个线程运行(node.getAndUnsetStatus(COND) & COND) != 0为true,只有它的getAndUnsetStatus(COND) 返回值包含COND位。之后的线程的getAndUnsetStatus(COND) 返回值都不包含COND位,(node.getAndUnsetStatus(COND) & COND) != 0结果永远是false,代表当前节点已经不在等待队列。
唤醒 signal与signalAl
public final void signal() {ConditionNode first = firstWaiter;if (!isHeldExclusively())throw new IllegalMonitorStateException();if (first != null)doSignal(first, false);
}public final void signalAll() {ConditionNode first = firstWaiter;if (!isHeldExclusively())throw new IllegalMonitorStateException();if (first != null)doSignal(first, true);
}private void doSignal(ConditionNode first, boolean all) {while (first != null) {ConditionNode next = first.nextWaiter;if ((firstWaiter = next) == null)lastWaiter = null;if ((first.getAndUnsetStatus(COND) & COND) != 0) {enqueue(first);if (!all)break;}first = next;}
}
signal()方法和signalAll()方法类似,都执行参数校验,并且交给doSignal()方法执行。
doSignal()方法将节点从等待队列移除,放入同步队列。并没有主动唤醒节点线程。
(first.getAndUnsetStatus(COND) & COND) != 0=true则当前线程是第一个去除first节点状态COND的线程。因此可以执行enqueue()方法将节点放入同步队列。
如果(first.getAndUnsetStatus(COND) & COND) != 0=false则代表当前线程从ConditionNode first = firstWaiter语句到本条语句期间,first节点的状态已经被别的线程更改了, 跳过当前节点,尝试更改下一个节点。
signalAll()方法将all=true,代表所有等待节点都会被放入同步队列。
相关文章:
JDK17 AbstractQueuedSynchronizer 二 条件队列
条件队列 同步队列中的线程是为了争抢锁,而条件队列中的线程是主动释放锁,挂起自己,等条件满足时被别的线程唤醒,继续工作。 AQS里只有1个同步队列,但可以有多个等待队列,每个等待队列对应一个ConditionO…...
8 设计模式之简单工厂模式
设计模式是软件开发中的一套通用解决方案,而简单工厂模式则是最基础、最常用的一种创建型模式。在这篇博客中,我将为大家详细介绍简单工厂模式的概念、优缺点,以及通过一个饮料制作的案例,帮助大家更好地理解和应用这种模式。 一、…...
计算机的错误计算(一百六十九)
摘要 探讨 MATLAB 中一个不动点的计算精度问题。 不动点是一类特殊的循环迭代。它有形式 例1. 已知迭代[1] 计算 显然,每个 均为 0.5 . 下面看看 MATLAB 的计算结果。不妨不用循环语句,直接用算术表达式表示 这时计算结果在如下图片: …...
Android 图形系统之三:SurfaceControl
在 Android 系统中,SurfaceControl 是一个关键的类,用于管理应用窗口和屏幕上的显示内容。它与 SurfaceFlinger 紧密交互,通过 BufferQueue 提供高效的图形缓冲区管理能力。SurfaceControl 是 Android 的显示架构中不可或缺的部分,…...
Laravel8.5+微信小程序实现京东商城秒杀方案
一、商品秒杀涉及的知识点 鉴权策略封装掊口访问频次限制小程序设计页面防抖接口调用订单创建事务使用超卖防御 二、订单库存系统方案(3种) 下单减库存 优点是库存和订单的强一致性,商品不会卖超,但是可能导致恶意下单ÿ…...
Makefile 入门指南:构建自动化编译流程
个人主页:chian-ocean 文章专栏 前言 make 和 Makefile 是编译和构建软件项目时非常常用的工具和文件,它们通常配合使用来自动化项目的编译过程。 make 定义:make 是一个构建自动化工具,用于根据项目文件的依赖关系自动完成编译…...
C#热更原理与HybridCLR
一、Mono的诞生 在Mono之前,C#虽然很好,但是只在windows家族平台上使用,就这点C#与Java就无法比。于是微软公司向ECMA申请将C#作为一种标准。在2001年12月,ECMA发布了ECMA-334 C#语言规范。C#在2003年成为一个ISO标准(ISO/IEC 23270)。意味着只要你遵守CLI(Common Lang…...
里氏替换原则:Java面向对象设计的基石
在面向对象编程(OOP)中,继承是一个强大的工具,它允许我们创建新的类(子类)来复用和扩展现有类(父类)的功能。然而,继承也带来了复杂性,特别是在确保子类能够正…...
恒创科技:服务器操作系统和客户端操作系统之间的区别
客户端操作系统和服务器操作系统是两种不同的操作系统,旨在满足计算机网络环境中的特定目的。虽然每种类型的操作系统在基本功能方面都有一些相似之处,但它们针对不同的用例进行了优化,并具有针对其特定角色量身定制的特定功能。 什么是服务器…...
做异端中的异端 -- Emacs裸奔之路4: 你不需要IDE
确切地说,你不需要在IDE里面编写或者阅读代码。 IDE用于Render资源文件比较合适,但处理文本,并不划算。 这的文本文件,包括源代码,配置文件,文档等非二进制文件。 先说说IDE带的便利: 函数或者变量的自动…...
Unity3d C# 摄像头检测敌方单位(目标层级)并在画面中标注(含源码)
前言 需要实现的功能是通过一个专门的检测摄像头将出现在摄像头画面内的敌方单位检测出来,并通过框选的UI框在画面中标记出来。检测摄像头支持自动检测和手动控制检测,同时需要实现锁定模式,检测到一个敌方单位直接锁定到对象上等功能。 效…...
js 16进制加密
function hexEncode(str) { let hexEncodedStr ‘’; for (let i 0; i < str.length; i) { let charCode str.charCodeAt(i); let hexCode charCode.toString(16).padStart(2, ‘0’); hexEncodedStr ‘\x’ hexCode; } return hexEncodedStr; } // 示例用法 let ori…...
性能测试之压测
1、首先需要提前准备好需要压测的接口地址及对应的接口参数 写好对应的压测接口及对应参数脚本 2、添加线程组(根据对应的需求提供的QPS及需要压测的数量如有) 如:40个线程,循环次数为永远(或者根据自身情况设置循…...
CentOS修改yum.repos.d源,避免“Could not resolve host: mirrorlist.centos.org”错误
1、问题现象 由于CentOS停止维护,mirrorlist.centos.org网站也关闭不可访问。导致CentOS默认配置的yum.repos.d源也不可用,所以执行yum命令会报“Could not resolve host: mirrorlist.centos.org”错误。具体如下: Could not retrieve mirror…...
Python 三目运算实战详解
Python 的三目运算符(也称为条件表达式)是一种简洁的方式来执行基于条件的赋值或返回值。它的语法类似于其他编程语言中的三元运算符,但有一些细微的不同。在 Python 中,三目运算符的语法如下: value_if_true if cond…...
JVM 性能调优 -- CMS 垃圾回收器 GC 日志分析【Full GC】
前言: 上一篇我们分析了 Minor GC 的发生过程,因为 GC 日志没有按我们预估的思路进行打印,其中打印了 CMS 垃圾回收器的部分日志,本篇我们就来分析一下 CMS 垃圾收集日志。 JVM 系列文章传送门 初识 JVM(Java 虚拟机…...
PS的学习
背景差色较大,就魔棒 魔棒的连续就是倒水点的跨越问题 魔棒的容差的选择就有点看经验了,看颜色的统一程度选择 Ctrl D 取消当前所有的选区 至于快速选择工具,和对象选择工具也差不多,只不过控制范围变成了一块一块的&#x…...
数据集搜集器(百科)008
对数据集搜集器(百科)007进行一下改进: 错误处理:增加更多的错误处理,比如网络请求超时、解析错误等。 用户界面:增加一些提示信息,让用户更清楚当前的操作状态。 多线程处理:确保多…...
Java学习,反射
Java反射是Java编程语言的一个重要特性,它允许程序在运行时查看任意对象所属的类,获取类的内部信息(包括构造器、字段和方法等),并能动态地调用对象的方法或构造器。 反射概念 反射(Reflection)…...
数据结构 (18)数的定义与基本术语
前言 数据结构是计算机科学中的一个核心概念,它描述了数据元素之间的关系以及这些元素在计算机中的存储方式。 一、数的定义 在计算机科学中,“数”通常指的是树形数据结构,它是一种非线性的数据结构,由节点(或称为元素…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
