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 为例,进入到 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

虚幻学习笔记7—蓝图接口

一、前言 蓝图接口就是可以在蓝图中实现的接口,有它方便的地方,可以很方便的调用到实现了接口的函数。 二、实现 2.1、创建一个蓝图接口 1)可以添加多个函数。 2)函数在蓝图接口中只能规定输入和输出参数。 只有输入参数的可以…

OSHI-操作系统和硬件信息库

文章目录 引言一、快速入门1.1 OSHI的简介1.2 引入依赖1.3 涉及的包(package)1.4 涉及的核心类 二、操作系统信息:OperatingSystem2.1 总揽2.2 文件系统信息:FileSystem2.3 网络参数信息:NetworkParams2.4 进程信息&am…

手势识别4:C/C++实现手部检测和手势识别(含源码下载)

手势识别4:C/C实现手部检测和手势识别(含源码下载) 目录 手势识别4:C/C实现手部检测和手势识别(含源码下载) 1. 前言 2. 手势识别模型(YOLOv5) (1)手势识别模型训练 (2)将Pyto…

家电产品扇叶零部件自动化三维检测设备高精度3D测量系统-CASAIM-IS(2ND)

一、背景介绍 某家电制造商希望对其生产的家电产品零部件进行高精度的3D测量,以确保零部件的尺寸精度和质量符合严格的标准,零部件的形状复杂且多样化,对于一些细节部位的测量精度要求极高。本文将介绍CASAIM-IS(2ND)…

【Seata源码学习 】篇五 注册分支事务

【Seata源码学习 】篇五 分支事务注册 1.远程服务调用绑定XID 回到事务模版方法类TransactionalTemplate中 beginTransaction(txInfo, tx);Object rs;try {// Do Your Business// 执行执行拦截器链路rs business.execute();} catch (Throwable ex) {// 3. The needed busine…

【java】图书管理系统

完整代码链接:https://gitee.com/zeng-xuehui/Java_repository/tree/master/test_11_27_1/src我们在写这个系统时,首先需要搭建框架,再实现业务逻辑;图书管理系统是用户通过各种功能对图书进行操作的一个系统;我们需要…

Google play开发者账号付款资料暂停的原因及解决方案

相信大多数Google play开发者都收到过这封邮件 邮件内容的大致意思是“由于可疑的活动,我们暂停了你的付款资料。” “要恢复您的帐户,请转到您的帐户并执行所要求的操作。” 这是触发了谷歌的付款风控机制,根据开发者们的反馈,账…

Making Reconstruction-based Method Great Again for Video Anomaly Detection

Making Reconstruction-based Method Great Again for Video Anomaly Detection 文章信息: 发表于ICDM 2022(CCF B会议) 原文地址:https://arxiv.org/abs/2301.12048 代码地址:https://github.com/wyzjack/MRMGA4VAD…

深入理解 Vue 中的指针操作(二)

文章目录 ☘️引言☘️基本用法🍂v-for指令🍂v-model指令🌱v-model适用表单控件 ☘️结论 ☘️引言 Vue.js 是一款非常流行且功能强大的前端框架,它以其响应式的数据绑定和组件化的开发方式赢得了众多开发者的喜爱。而在 Vue.js …

泗博Modbus转Profinet网关TS-180对水表流量的监控应用

应用场景: 陕西某工程技术有限公司在一民生工程项目中,需要对公园直饮水进行净化保证其水质。直饮水净化装置需根据用水量不定期的维护,通过统计各个净化装置净化的直饮水的流量,来实现提前维护目的。 应用痛点: 项目…

二叉树(判断是否为单值二叉树)

题目(力扣): 判断二叉树上每个节点的值是否相同,就需要让root节点分别与左节点和右节点分别比较是否相同。 注意:root等于空时,直接可以返回true; 首先,先判断他的特殊情况&#x…

C++ day38 动态规划 斐波那契数列 爬楼梯 使用最小花费爬楼梯

题目1&#xff1a;509 斐波那契数列 题目链接&#xff1a;斐波那契数列 对题目的理解 斐波那契数列由0和1开始&#xff0c;后面每一项数字(F(n))都是前两个数字的和&#xff0c;给一个n&#xff0c;计算F(n)&#xff0c;&#xff08;0<n<30&#xff09; 动规五部曲 …

“Install Js dependencies failed“JS SDK安装失败【Bug已解决-鸿蒙开发】

文章目录 项目场景:问题描述原因分析:解决方案:解决措施1解决方案2:其他解决方案解决方案3:此Bug解决方案总结项目场景: 在下载JS SDK时,出现下载失败的情况,并显示“Install Js dependencies failed”。 在使用版本为DevEco Studio 3.0.0.601 Beta1进行低代码开发时…

Echarts-使用渐变色填充

垂直方向的渐变 color: {type: linear,// x0,y1,柱子的颜色在垂直方向渐变x: 0,y: 1,colorStops: [// 0%处的颜色{offset: 0,color: #12c2e9,},// 50%处的颜色{offset: 0.5,color: #c471ed,},// 100%处的颜色{offset: 1,color: #f64f59,},],global: false // 缺省为 false} 水…

力扣347. 前 K 个高频元素(java,最小堆,快速排序法)

Problem: 347. 前 K 个高频元素 文章目录 前言题目描述思路解题方法复杂度Code 前言 对于求取Top K一般有如下两种题型&#xff1a; 1.针对静态数据&#xff08;查询TopK操作&#xff09; 2.针对动态数据&#xff08;包括添加数据操作和查询TOPK操作&#xff09; 一般解决思路…

第22章 NIO编程

在本章中需要掌握NIO中的缓冲区的作用&#xff0c;并理解缓冲区中的数据处理模型&#xff0c;掌握Channel的作用&#xff0c;并结合缓冲区实现数据I/O操作&#xff0c;理解文件锁的作用&#xff0c;并且掌握字符编码处理支持类的使用&#xff0c;掌握Reactor设计模型&#xff0…

Electron+Ts+Vue+Vite桌面应用系列:sqlite增删改查操作篇

文章目录 1️⃣ sqlite应用1.1 sqlite数据结构1.2 初始化数据库1.3 初始化实体类1.4 操作数据类1.5 页面调用 优质资源分享 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418 ElectronTsVueVite桌面应用系列 &#xff1a;这个系列包括了从桌…

【设计模式-2.2】创建型——简单工厂和工厂模式

说明&#xff1a;本文介绍设计模式中&#xff0c;创建型设计模式中的工厂模式&#xff1b; 飞机大战 创建型设计模式&#xff0c;关注于对象的创建&#xff0c;本文介绍的简单工厂和工厂模式同样也是。举一个游戏例子&#xff0c;如飞机大战游戏中&#xff0c;屏幕中敌人类型…

【开题报告】海洋多源数据质量控制应用服务的WebServer设计与实现

开 题 报 告 内 容 论文选题的意义、主要研究内容和文献资料调研情况 一、选题意义 在当今世界研究自然环境的大背景下&#xff0c;计算机技术与各学科、各领域的综合应用逐渐增多。作为地球上最广阔的水体&#xff0c;同时也是地球上决定气候发展的主要的因素之一&#xff0…

Unity学习笔记11

一、视频播放功能 1.如何让视频在游戏场景中播放&#xff1f; 在Assets目录下添加一个渲染器纹理&#xff0c;步骤&#xff1a;新建→渲染器纹理 首先在创建一个平面&#xff0c;想让视频在平面上显示。在平面上添加一个组件 Video Player 然后将视频文件拖拽到视频剪辑位置上…