Condition 源码解析
Condition 源码解析
文章目录
- Condition 源码解析
- 一、Condition
- 二、Condition 源码解读
- 2.1. lock.newCondition() 获取 Condition 对象
- 2.2. condition.await() 阻塞过程
- 2.3. condition.signal() 唤醒过程
- 2.4. condition.await() 被唤醒后
- 三、总结
一、Condition
在并发情况下进行线程间的协调,如果是使用的 synchronized
锁,我们可以使用 wait()/notify()
进行唤醒,如果是使用的 Lock
锁的方式,则可以使用 Condition
进行针对性的阻塞和唤醒,相较于 wait()/notify()
使用起来更灵活。那么 Condition
是如何实现线程的等待和唤醒的呢,本文通过解析Condition
的源码进行理解。
在进行源码分析前,先通过一个案例看下 Condition
是如何使用的
public class Test {public synchronized static void main(String[] args) throws InterruptedException {Lock lock = new ReentrantLock();Condition condition = lock.newCondition();new Thread(() -> {lock.lock();System.out.println("线程1开始等待!");try {condition.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1被唤醒继续执行结束!");lock.unlock();}, "1").start();new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}lock.lock();System.out.println("开始唤醒线程!");condition.signal();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程2执行结束!");lock.unlock();}, "2").start();}
}
运行之后,可以看到下面日志:
由于在第一个线程中,使用的 condition.await()
因此当前线程会被阻塞挂起,而第二个线程,在 1s
后进行了 condition.signal()
操作,因此第一个线程会被唤醒继续执行。这里可以发现,第一个线程阻塞时锁并没有释放,而第二个线程在1s
后也成功拿到锁了,所以表明在 condition.await()
时会自动释放当前锁,这点和 wait()
相同,在第二个线程进行了 condition.signal()
操作,第一个线程并没有继续向下执行,而是等待第二个线程处理完才会继续执行,由此可以表明被唤醒的线程会重新获取锁,成功获取锁后继续执行。
下面通过源码看下 Condition
是如何实现的等待唤醒。
二、Condition 源码解读
2.1. lock.newCondition() 获取 Condition 对象
首先看下在使用 lock.newCondition()
获取一个Condition
对象时,具体做了什么,这里以 ReentrantLock
为例,进入到 ReentrantLock
的 newCondition()
方法中,又执行了 Sync
的 newCondition()
方法,再进去就会发现其实是 new
了一个 ConditionObject
类对象:
下面点到这个类中,可以看到其实是 AQS
下的一个子类:
2.2. condition.await() 阻塞过程
了解到 Condition
的对象后,可以看到是 AQS
下的一个子类,那下面其他的方法也肯定依赖于 AQS
,下面看下 condition.await()
方法,点到 await()
方法中:
其中 addConditionWaiter()
则是将自己加入到 AQS
的队列中,并获取到当前线程所在的 Node
,这里注意下 Node
的状态是 Node.CONDITION
也就是 -2
,后面会依赖于该状态。
再回到 await()
方法继续向下看,接着使用了 fullyRelease()
方法传入了当前的 Node
,这里的 fullyRelease()
方法主要做了释放当前线程锁的操作,可以看到又调用了 AQS
的 release()
进行释放资源,也就是释放了当前所持有的锁。
回到 await()
方法中,当释放锁后,下面进入到了 while
循环中,通过查看 isOnSyncQueue()
方法,可以看到是符合while
的条件也就可以进入到循环中:
在循环中可以明显的看到 LockSupport.park(this)
,将当前线程进行了阻塞。
2.3. condition.signal() 唤醒过程
上面已经看到线程被阻塞了,如果需要被唤醒则需要通过condition.signal()
,这个方法是如何唤醒的呢?
下面来到 AbstractQueuedSynchronizer
类的 signal()
方法中:
主要执行了 doSignal()
方法,再点到 doSignal()
中,可以看到这里开启了一个循环,对链表的每一个元素都进行了 transferForSignal()
操作,这里也比较好理解,就是要唤醒等待中的线程。
下面点到 transferForSignal()
中,看下对每个 Node
都做了什么操作。点进去之后也比较好理解,如果状态是 Node.CONDITION
也就是 -2
,刚才在解读 await()
方法时就提到这个状态了,这里正好形成了呼应,下面有个非常显眼的操作 LockSupport.unpark(node.thread)
直接唤醒了目标线程。也就是唤醒了 2.2 中的最后一步操作。
2.4. condition.await() 被唤醒后
当 await()
方法中的 LockSupport.park(this)
被唤醒后,继续向下执行,下面会判断下当前线程有没有被打断,如果没被打断则 break
终止循环继续执行。
下面会使用 AQS
的 acquireQueued()
方法,将先进入队列的线程进行抢占锁资源,如果成功获取锁后就会继续执行,如果抢占失败则继续被挂起阻塞。
三、总结
通过上面的源码分析,应该对 Condition
有了新的理解和掌握,在源码中好多地方都使用了 CAS
,因此当竞争资源非常激烈时, Lock
的性能要远远优于 synchronized
。
相关文章:

Condition 源码解析
Condition 源码解析 文章目录 Condition 源码解析一、Condition二、Condition 源码解读2.1. lock.newCondition() 获取 Condition 对象2.2. condition.await() 阻塞过程2.3. condition.signal() 唤醒过程2.4. condition.await() 被唤醒后 三、总结 一、Condition 在并发情况下…...
acwing算法基础之数学知识--求组合数进阶版
目录 1 基础知识2 模板3 工程化 1 基础知识 请明确如下关于取余的基本定理: 数a和数b的乘积模上p,等于数a模上p和数b模上p的乘积。即, ( a ⋅ b ) m o d p ( a m o d p ) ⋅ ( b m o d p ) (a \cdot b ) \ mod \ p (a \ mod \ p) \cdot …...
基础算法:大数除以除以13
基础算法:大数除以一个数 信息学奥赛:1175:除以13 时间限制: 1000 ms 内存限制: 65536 KB 【题目描述】 输入一个大于0的大整数N,长度不超过100位,要求输出其除以13得到的商和余数。 【输入】 一个大于0的大整数&…...
软件版本区分
引言 定义好版本号对于产品的版本发布与持续更新很重要但是对于版本怎么定义规则如何确定却是千差万别。具体应用可以结合自己目前的实际情况命名。另外对于商业软件有的产品号称是永远的Beta版持续不断地更新、优化迭代产品才有生命力。 ⭕ 软件版本周期 α、β、λ 常用来…...

Redis高可用之主从复制及哨兵模式
一、Redis的主从复制 1.1 Redis主从复制定义 主从复制是redis实现高可用的基础,哨兵模式和集群都是在主从复制的基础之上实现高可用; 主从复制实现数据的多级备份,以及读写分离(主服务器负责写,从服务器只能读) 1.2 主从复制流…...

代理模式,dk动态代理,cglib动态代理
目录 一、代理模式1、生活中代理案例2、为什么要使用代理3、代理模式在Java中的应用4、什么是代理模式 二、代理的实现方式1、java中代理图示2、静态代理 三、动态代理1、概述2、JDK动态代理jdk动态代理原理分析 3、Cglib动态代理3.1 基本使用3.2 cglib基本原理 一、代理模式 …...

Vue2系列 -- 组件自动化全局注册(require.context)
参考官网:https://v2.cn.vuejs.org/v2/guide/components-registration.html 1 作用 省略 import 引入组件 省略 在main.js 中注册 实现自动化引入组件 2 自定义文件夹 在 src 下新建一个 components/base 文件夹,用于存放要自动注册的组件 3 在 base…...

【华为OD题库-038】支持优先级的对列-java
题目 实现一个支持优先级的队列,高优先级先出队列,同优先级时先进先出。 如果两个输入数据和优先级都相同,则后一个数据不入队列被丢弃。 队列存储的数据内容是一个 整数。 输入描述 一组待存入队列的数据(包含内容和优先级)。 输出描述 队列…...

python爱心代码高级
在Python中,我们可以使用matplotlib库来创建一个更高级的爱心图形。以下是一个示例: import matplotlib.pyplot as pltimport numpy as npx np.linspace(-2, 2, 1000)y1 np.sqrt(1-(abs(x)-1)**2)y2 -3*np.sqrt(1-(abs(x)/2)**0.5)fig, ax plt.subp…...

基于SSM+Vue的社区共享食堂管理系统
基于SSM的社区共享食堂管理系统的设计与实现~ 开发语言:Java数据库:MySQL技术:SpringMyBatisSpringMVC工具:IDEA/Ecilpse、Navicat、Maven 系统展示 主页 菜品详情 管理员界面 摘要 社区共享食堂管理系统是一种基于SSM…...
MYSQL基础知识之【修改数据,删除数据】
文章目录 前言MySQL UPDATE 查询使用PHP脚本更新数据 MySQL DELETE 语句从命令行中删除数据使用 PHP 脚本删除数据 后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:Mysql 🐱👓博主在前端领域还有很多知识和技术…...
【机器学习】交叉验证 Cross-validation
交叉验证(CrossValidation)方法思想简介 以下简称交叉验证(Cross Validation)为CV.CV是用来验证分类器的性能一种统计分析方法,基本思想是把在某种意义下将原始数据(dataset)进行分组,一部分做为训练集(train set),另一部分做为验证集(validation set),首先用训练集对分类器进…...

Pycharm修改文件默认打开方式 + CSV Editor插件使用
1、File —> Settings —> Editor —> File Types 然后将*csv添加到最上面 在plugins中下载插件,CSV Editor 备注:不在上一步的“File Types”中将*.csv设置为CSV格式,插件是不起作用的 就可以使用了...

shiro整合redis
shiro整合redis 前言:shiro默认的session是存储在jvm内存中的,这样会导致java服务内存占用更大以及一旦服务器宕机或者版本迭代需要重启服务时,缓存中的数据不能恢复,导致用户需要重新登录认证,体验很差。因此利用第三…...
HarmonyOS(七)——@BuilderParam装饰器
前言: 前面我们认识了Builder装饰器:自定义构建函数,今天我们继续认识下一个装饰器——BuilderParam装饰器。 当开发者创建了自定义组件,并想对该组件添加特定功能时,例如在自定义组件中添加一个点击跳转操作。若直接…...

展开运算符(...)
假如我们有一个数组: const arr [7,8,9];● 我们如果想要数组中的元素,我们必须一个一个手动的去获取,如下: const arr [7,8,9]; const badNewArr [5, 6, arr[0], arr[1],arr[2]]; console.log(badNewArr);● 但是通过展开运…...

Apache Flink(二):数据架构演变
🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹…...

【C++】类与对象(中)
一、类的默认成员函数 如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。 默认成员函数:用户没有显式实现,编译器会自…...

webshell之无扩展免杀
1.php加密 这里是利用phpjiami网站进行加密,进而达到加密效果 加密前: 查杀效果 可以看到这里D某和某狗都查杀 里用php加密后效果 查杀效果 可以看到这里只有D某会显示加密脚本,而某狗直接绕过 2.dezend加密 可以看到dezend加密的特征还是…...

用 VirtualBox 安装 OpenWrt 等 Linux 系统,无法启动的解决办法
用 VirtualBox 安装 OpenWrt 等 Linux 系统,无法启动的解决办法 最近新买了台联想小新 Pro 14 2023 锐龙版,因为有 32GB 的运行内存,所以想安装虚拟机以充分发挥。一开始使用 Hyper-V 来安装可以正常使用,但是后面想使用 Virtual…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
Python实现简单音频数据压缩与解压算法
Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中,压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言,提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...
【大厂机试题+算法可视化】最长的指定瑕疵度的元音子串
题目 开头和结尾都是元音字母(aeiouAEIOU)的字符串为元音字符串,其中混杂的非元音字母数量为其瑕疵度。比如: “a” 、 “aa”是元音字符串,其瑕疵度都为0 “aiur”不是元音字符串(结尾不是元音字符) “…...