Condition 源码解析

news2024/11/25 9:29:37

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 为例,进入到 ReentrantLocknewCondition() 方法中,又执行了 SyncnewCondition() 方法,再进去就会发现其实是 new 了一个 ConditionObject 类对象:
在这里插入图片描述

下面点到这个类中,可以看到其实是 AQS 下的一个子类:
在这里插入图片描述

2.2. condition.await() 阻塞过程

了解到 Condition 的对象后,可以看到是 AQS 下的一个子类,那下面其他的方法也肯定依赖于 AQS ,下面看下 condition.await() 方法,点到 await() 方法中:

在这里插入图片描述

其中 addConditionWaiter() 则是将自己加入到 AQS的队列中,并获取到当前线程所在的 Node ,这里注意下 Node 的状态是 Node.CONDITION 也就是 -2,后面会依赖于该状态。

在这里插入图片描述在这里插入图片描述

再回到 await() 方法继续向下看,接着使用了 fullyRelease() 方法传入了当前的 Node ,这里的 fullyRelease() 方法主要做了释放当前线程锁的操作,可以看到又调用了 AQSrelease() 进行释放资源,也就是释放了当前所持有的锁。
在这里插入图片描述

回到 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 终止循环继续执行。
在这里插入图片描述
在这里插入图片描述

下面会使用 AQSacquireQueued() 方法,将先进入队列的线程进行抢占锁资源,如果成功获取锁后就会继续执行,如果抢占失败则继续被挂起阻塞。

在这里插入图片描述
在这里插入图片描述

三、总结

通过上面的源码分析,应该对 Condition 有了新的理解和掌握,在源码中好多地方都使用了 CAS ,因此当竞争资源非常激烈时, Lock 的性能要远远优于 synchronized

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1267788.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux 之iptables

1.iptables防火墙基本介绍 Linux系统的防火墙:IP信息包过滤系统,它实际上由两个组件 netfilter和 iptables 组成。 主要工作在网络层,针对IP数据包。体现在对包内的IP地址、端口、协议等信息的处理上。 iptables由软件包iptables提供的命令…

诊所电子处方范例之配方模板如何添加到处方中操作教程

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么?二、使用步骤 1.引入库2.读入数据总结 前言 诊所门诊在开电子处方的时候,如果能使用模板一键导入,则可以节…

8个程序员常用的开发工具,各位老铁请收好!

Chat GPT的升级节奏让人们越来越惊讶的同时,也让大家感觉到了压力,在如此快节奏的互联网世界中,开发人员需要不断学习与更新知识,保持领先地位并高效地交付高质量软件。 无论是集成开发环境 (IDE)、版本控制系统、测试工具、协作…

二阶常系数非齐次线性微分方程:类型二

或 (1) (2) 欧拉公式 (3) 设(3)有特解 是(1)的特解 是(2)的特解 例 : 解 所以其通解为 这是属于上一篇提到的第一种情况 其特解 即 简化 -2a1 a-1/2 原方程特解为 原方程通解为 解法二 将 或 改写为 设是特征方程的k重根&a…

VR特警野外武装仿真虚拟训练实操教学保证训练效果

特警VR模拟仿真训练软件的优势主要体现在以下几个方面: 真实感和沉浸感:通过VR技术,特警可以在虚拟环境中体验真实的训练场景,如人质解救、反恐行动等。这种真实感和沉浸感可以帮助特警更好地理解和适应实际情况,提高训…

双指针算法(题目与答案讲解)

文章目录 题目移动零复写零两数之和N数之和(>2个数) 答案讲解移动零复写零两数之和N数之和 题目 力扣 移动零 1、移动零:题目链接 复写零 2、复写零:题目链接 两数之和 3、两数之和题目链接 N数之和(>2个数) 4、N数之和(三个数、四个数) 三个数:题目链接 四个数题目链接…

el-table合并行

需求: 1、”用户任务“中的”代码“需要按照升序进行排列; 2、”用户任务“中连续的”会签“是共用一个序号,并且序号进行合并 效果 解决方法 列表排序一般是前端传值,后端进行排序。由于后端返回的表格列表没有 序号indexNum …

【华为数通HCIP | 网络工程师】821刷题日记-BFD和VRRP 及重点(2)

个人名片: 🐼作者简介:一名大三在校生,喜欢AI编程🎋 🐻‍❄️个人主页🥇:落798. 🐼个人WeChat:hmmwx53 🕊️系列专栏:🖼️…

jmeter多个接口测试

针对接口文档,进行对应接口设计,多个接口设计用例需要使用事物控制器。 1.通过登录接口提取sign值 发送一个登录请求,然后通过正则表达式提取该sign值 正则表达式的使用,我稍后会在下一个博文中详细说明,这边就不多说…

算法中的时间复杂度,空间复杂度

一、前言 算法(Algorithm)是指用来操作数据、解决程序问题的一组方法。对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但在过程中消耗的资源和时间却会有很大的区别 衡量不同算法之间的优劣主要是通过时…

量子芯片:引领计算技术的新篇章

量子芯片:引领计算技术的新篇章 引言 随着量子计算的飞速发展,量子芯片作为量子计算机的核心组件,日益受到人们的关注。量子芯片的出现,不仅有望推动计算技术的革新,更将在信息安全、药物研发、金融投资等领域掀起巨大的变革。在本篇博客中,我们将深入探讨量子芯片的原理…

windows环境下载安装Nginx并配置防火墙

1、下载Nginx Nginx官网 下载稳定版 2、下载之后,解压 3、启动Nginx,命令:start nginx 最小化该窗口 主要,不要关闭,如果关闭,表示nginx服务关闭了 4、测试是否启动成功 在浏览器中输入http://localhos…

2023.11.26使用opencv调节图片亮度

2023.11.26使用opencv调节图片亮度 测试一些opencv对图片的处理效果,方法比较简单,找出所有像素点,然后将RGB三色的亮度分别进行调节即可,同类可以进行像素级的处理。测试结果和项目代码如下: 使用OpenCV调节图拍亮…

测试必会+面试必问--fiddler参数详解

Fidder抓包是作为测试必须掌握的一项技能。 这篇详细介绍(保姆级)Fiddler在测试中常用的功能。 全文较长(6000字),建议先收藏,需要时再食用。 一、界面简介 先看一下完整的Fiddler界面布局 Fiddler界面…

微信发红包,有哪些测试点

1、功能 1.在红包钱数,和红包个数的输入框中只能输入数字 2.红包里最多和最少可以输入的钱数 200 0.01 3.拼手气红包最多可以发多少个红包 100 3.1超过最大拼手气红包的个数是否有提醒 4.当红包钱数超过最大范围是不是有对应的提示 5.当发送的红包个数超过…

Kubernetes技术与架构-安全性

本文主要从不同层面与多个维度描述Kubernetes技术与架构的安全性。 云原生的安全性 从系统分层架构的角度分析,自底向上,云原生的安全性主要包括云、集群、容器以及代码四个层面,简称云原生4C安全,其架构图如下所示:…

express+multer实现简单的文件上传功能

expressmulter实现简单的文件上传功能 1.安装multer和uuid依赖 cnpm install -S uuid multer2.添加multer的配置文件 在config文件夹下添加uploa.js文件,内容如下: // 引入multer const multer require(multer) // uuid : 用于生成不重复的由英文组…

第20 章 多线程

20.1线程简介. 20.2创建线程 2.1继承Thread类 Thread 类是java.lang包中的一个类,从这个类中实例化的对象代表线程,程序员启动一个新线程需要建立Thread 实例。Thread类中常用的两个构造方法如下: public Thread():创建一个新的线程对象。 public Threa…

BTCPay Server:免费、安全、开源的比特币支付处理器 | 开源日报 No.90

MunGell/awesome-for-beginners Stars: 58.0k License: NOASSERTION 这个项目是一个收集开源项目的列表,旨在帮助初学者找到可以贡献代码的机会。该列表按编程语言分类,并列出了每个项目以及其标签 (如 “good-first-issue”、“beginner” 等)。主要功…

宇宙中可能存在的另外一种生命体-硅基生命

本文提到的所有图片有燧原曜图平台生成提供 概念 碳基:生命的组织架构由碳元素组成(地球的生命)硅基:生命的组织架构由硅元素组成 引言 费米悖论 中提到有存在外星文明的可能,但它们迄今为止还无法和我们接触三体人…