Condition 源码解读
一、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
类对象:
下面点到这个类中,可以看到其实是 AbstractQueuedSynchronizer
下的一个子类:
2.2. condition.await() 阻塞过程
了解到 Condition
的对象后,下面就可以看下 condition.await()
方法了,点到该类下的 await()
方法中:
其中 addConditionWaiter()
则是将自己加入到链表中,并获取到当前线程所在的 Node
,这里注意下 Node
的状态是 Node.CONDITION
也就是 -2
,后面会依赖于该状态。
下面再回到 await()
方法继续向下看,接着使用了 fullyRelease
方法传入了当前的 Node
,这里的 fullyRelease
方法主要做了释放当前线程锁的操作。
点到 release
方法中,主要执行了 unparkSuccessor
,如果看过 Lock
锁的解锁源码,就会知道其实 unparkSuccessor
就是解锁的过程
下面继续回到 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
终止循环继续执行。
下面这个 acquireQueued
方法,如果看过 Lock
加锁的源码,应该可以了解到就是上锁的过程
成功获取锁后就会继续执行,被阻塞的线程也就是继续执行。
三、总结
通过上面的源码分析,应该对 Condition
有了新的理解和掌握,细心地小伙伴应该可以发现在源码中好多地方都使用了 CAS
,因此当竞争资源非常激烈时, Lock
的性能要远远优于 synchronized
。
相关文章:

Condition 源码解读
一、Condition 在并发情况下进行线程间的协调,如果是使用的 synchronized 锁,我们可以使用 wait/notify 进行唤醒,如果是使用的 Lock 锁的方式,则可以使用 Condition 进行针对性的阻塞和唤醒,相较于 wait/notify 使用…...

看完这篇入门性能测试
大家好,我是洋子。最近组内在进行服务端高并发接口的性能压测工作,起因是2023年2月2日,针对胡某宇事件进行新闻发布会直播,几十万人同时进入某媒体直播间,造成流量激增 从监控上可以看出,QPS到达某峰值后&…...

推导部分和——带权并查集
题解: 带权并查集 引言: 带权并查集是一种进阶的并查集,通常,结点i的权值等于结点i到根节点的距离,对于带权并查集,有两种操作需要掌握——Merge与Find,涉及到路径压缩与维护权值等技巧。 带…...

费解的开关/翻硬币
🌱博客主页:大寄一场. 🌱系列专栏: 算法 😘博客制作不易欢迎各位👍点赞⭐收藏➕关注 题目:费解的开关 你玩过“拉灯”游戏吗? 25盏灯排成一个 55 的方形。 每一个灯都有一个开关&…...

OpenGL中的坐标系
1、2D笛卡尔坐标系2D笛卡尔坐标系跟我们高中的时候学习的坐标系一样,是由x、y决定的。2、3D笛卡尔坐标系3D笛卡尔坐标系坐标由x、y、z决定,满足右手定则。3、视口glViewport(GLint x,GLint y,GLsizei width,GLsizei height)窗口和视口大小可以相同&#…...

Spring——Spring介绍和IOC相关概念
Spring是以Spring Framework为核心,其余的例如Spring MVC, Spring Cloud,Spring Data,Spring Security SpringBoot的基础都是Spring Framework。 Spring Boot可以在简化开发的基础上加速开发。 Spring Cloud分布式开发 Spring有…...
A+B Problem
AB Problem 题目描述 输入两个整数 a,ba, ba,b,输出它们的和(∣a∣,∣b∣≤109|a|,|b| \le {10}^9∣a∣,∣b∣≤109)。 注意 Pascal 使用 integer 会爆掉哦!有负数哦!C/C 的 main 函数必须是 int 类型,…...

【ROS学习笔记11】ROS元功能包与launch文件的使用
【ROS学习笔记11】ROS元功能包与launch文件的使用 文章目录【ROS学习笔记11】ROS元功能包与launch文件的使用前言一、ROS元功能包二、ROS节点运行管理launch文件2.1 launch文件标签之launch2.2 launch文件标签之node2.3 launch文件标签之include2.4 launch文件标签之remap2.5 l…...
【python】
print函数 同时输出多行变量 print(a, b, sep\n) (23条消息) python3 中print函数参数详解,print(*values, sep , end\n, filesys.stdout, flushFalse)中参数介绍_sep,_phantom-dapeng的博客-CSDN博客 input() 输入浮点数,不能用int(input()) int()…...

充电协议: 快充协议,如何选充电宝?
快充协议(存在两种:电压; 电流) 目前市面上的快充技术大多遵循2个技术方向: 以高通QC、联发科PEP、华为FCP为首的高压低电流快充技术; 另一种就是以OPPO的VOOC以及华为SCP为首的低电压大电流快充技术。 目前常见的快充标准还有三星AFC、联发…...

视觉SLAM十四讲ch6 非线性优化笔记
视觉SLAM十四讲ch6 非线性优化笔记本讲目标上讲回顾状态估计问题非线性最小二乘Gauss-Newton:高斯牛顿Levenburg-Marquadt:列文伯格-马夸尔特小结实践:CERES实践:G2O本讲目标 理解最小二乘法的含义和处理方式。 理解Gauss-Newton…...
Nikto工具使用指南
NiktoNikto是一款开源网站服务器扫描器,使用Perl开发,可以对服务器进行全面扫描,包括6400多个潜在危险的文件/cgi(通用网关接口(Common Gateway Interface)),废话不多说,直接上命令:基本测试&am…...

Git(4)之基本工具
Git基础之基本工具 Author:onceday date:2023年3月5日 满满长路有人对你微笑过嘛… windows安装可参考文章:git简易配置_onceday_CSDN博客 參考文档: 《progit2.pdf》,Progit2 Github。《git-book.pdf》 文章目录…...
好书推荐。
个人喜欢看传记,散文,历史等 二战名人传记,苏联列宁,朱可夫,斯大林等 英国首相丘吉尔,美国富兰克林,中国毛泽东等 创业:比尔盖,扎克伯格,苹果公司创始人乔…...

[Pytorch]DataSet和DataLoader逐句详解
将自己的数据集引入Pytorch是搭建属于自己的神经网络的重要一步,这里我设计了一个简单的实验,结合这个实验代码,我将逐句教会大家如何将数据引入DataLoader。 这里以目标检测为例,一个batch中包含图片文件、先验框的框体坐标、目标…...

【Kettle-佛系总结】
Kettle-佛系总结Kettle-佛系总结1.kettle介绍2.kettle安装3.kettle目录介绍4.kettle核心概念1.转换2.步骤3.跳(Hop)4.元数据5.数据类型6.并行7.作业5.kettle转换1.输入控件1.csv文件输入2.文本文件输入3.Excel输入4.XML输入5.JSON输入6.表输入2.输出控件…...
JavaSE网络编程
JavaSE网络编程一、基本概念二、常用类三、使用方法1、创建服务器端Socket2、创建客户端Socket3、创建URL对象JavaSE中的网络编程模块提供了一套完整的网络编程接口,可以方便地实现各种基于网络的应用程序。本文将介绍JavaSE中网络编程模块的基本知识、常用类以及使…...

9万字“联、管、用”三位一体雪亮工程整体建设方案
本资料来源公开网络,仅供个人学习,请勿商用。部分资料内容: 1、 总体设计方案 围绕《公共安全视频监控建设联网应用”十三五”规划方案》中的总体架构和一总两分结构要求的基础上,项目将以“加强社会公共安全管理,提高…...
springboot自动装配原理
引言 springboot的自动装配是其重要特性之一,在使用中我们只需在maven中引入需要的starter,然后相应的Bean便会自动注册到容器中。例如: <dependency><groupId>org.springframework.boot</groupId><artifactId>spr…...

Docker学习(二十)什么是分层存储?
目录1.简介2.什么是 Union Mount?3.分层介绍1)lowerdir 层(镜像层)2)upperdir 层(容器层)3)merged 层4.工作原理1)读:2)写:3ÿ…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...