并发工具类(一):CountDownLatch

1、CountDownLatch介绍

      CountDownLatch 又称为“倒计数门阀”,但大多数称之为“计数器”,是juc包下的一个工具类;

      CountDownLatch 核心功能是:用于一个活多个线程等待其他线程执行完成的一组操作。

      CountDownLatch 中有个全局的计数器count,当前主线程会执行 CountDownLatch 的await

      方法进入阻塞状态,每当有一个子线程完成任务时,计数器就会减1,当count变为0时表示

      所有的子线程都已经执行完成,此时可以唤醒当前阻塞的主线程执行后续操作。

      CountDownLatch 是基于AQS的共享锁来完成线程的阻塞和唤醒,线程的阻塞也是阻塞在AQS

      的双向链表上的;采用AQS的全局变量state 来记录初始化计数器的数值。

2、核心属性&常用构造方法

      CountDownLatch 的核心属性只有一个,即 sync,Sync 是 CountDownLatch的内部类,

      其继承AQS,由构造方法可以知道CountDownLatch的基本操作底层实现都是基于AQS的

    //同步器private final Sync sync;public CountDownLatch(int count) {//todo 注意://   count 可以等于0,但等于0没有意义,若count==0,则await() 方法是不会挂起的if (count < 0) throw new IllegalArgumentException("count < 0");/*** 从这里可以看出,count初始化和核心操作均由同步器Sync完成*/this.sync = new Sync(count);}

      

3、使用场景

     有以下场景:

              如果有三个业务需要并行处理,而且需要知道3个业务全部处理完成了。

              对于这个场景,则可以通过  CountDownLatch 来实现。 

              给 CountDownLatch  设置一个数值3,然后创建3个线程来分别处理这3个任务;

              每个线程执行完成后,调用CountDownLatch的 countDown 方法让计数器减1,

              (计数器的值是3);主线程则可以在业务处理时调用 CountDownLatch 的await 方法

              ,让主线程进入挂起阻塞,直到 CountDownLatch 的计数器值编为0后,主线程才会被

             唤醒执行后续操作。如下图所示:

             

      

4、使用示例

      针对上边的场景,写个demo,展示 CountDownLatch 的基本用法

      示例代码如下:

public class CountDownLatchExample1 {public static void main(String[] args) throws InterruptedException {//先获取商品编号列表int[] products = getProductsByCategoryID();//使用Stream 流,将商品编号列表中的每个商品转换为 ProductPriceList<ProductPrice> list = Arrays.stream(products).mapToObj(ProductPrice::new).collect(Collectors.toList());//定义CountDownlatch,计数器的数量等于子任务个数CountDownLatch latch = new CountDownLatch(products.length);list.forEach(//为每个商品开启一个计算子任务pp -> new Thread(() -> {System.out.println(pp.getProdID()+" -> start calculate price.");try {//模拟调用其他系统耗时TimeUnit.SECONDS.sleep(current().nextInt(10));if(pp.prodID %2 == 0){pp.setPrice(pp.prodID*0.9D);}else {pp.setPrice(pp.prodID*0.71D);}System.out.println(pp.getProdID()+" -> start calculate completed.");} catch (InterruptedException e) {e.printStackTrace();}finally {//计数器CountDown,子任务执行完成latch.countDown();}}).start());//主线程阻塞,等待所有子任务结束,如果有一个子任务没有结束会一直等待//可以给阻塞加上超时时间latch.await();System.out.println("All of prices calculate finished");list.forEach(System.out::println);}//获取商品编号列表private static int[] getProductsByCategoryID(){//商品列表编号为从1,10的数字return IntStream.rangeClosed(1,10).toArray();}//定义商品类,有2个成员变量:商品编号和商品价格private static class ProductPrice{private final int prodID;//商品编号private double price;//商品价格public ProductPrice(int prodID){this(prodID,-1);}public ProductPrice(int prodID,double price){this.prodID = prodID;this.price = price;}public int getProdID(){return this.prodID;}public void setPrice(double price){this.price = price;}@Overridepublic String toString() {return "ProductPrice{" +"prodID=" + prodID +", price=" + price +'}';}}
}

5、常用方法解析

      CountDownLatch 中最常用的方法就3个,即 await、带超时时间的await、countDown 这三个

      方法,带超时时间的await方法与普通的await方法功能差不多,主要区别是带超时时间的await

      的方法被唤醒由2中方式,即:

              1)CountDownLatch计数器的值为0;

              2)等待时间超过了超时时间

      带超时时间的await方法实现流程就不看了,只看下普通的await方法。

5.1、await() 方法

        await 方法功能是判断当前 CountDownLatch 的计数器值是否为0,若为0,则直接唤醒阻塞

        的线程执行后续任务,若不为0,则将当前调用await()方法的线程以共享锁的方式放入AQS

        的双向链表进行阻塞,一直阻塞到 CountDownLatch 的计数器值是否为0 时被唤醒。

        await()方法代码如下:

              

        在await 方法内部,把业务委托给CountDownLatch内部类 Sync的方法

        acquireSharedInterruptibly 执行的。其中内部类Sync是 AQS 的一个子类,所以Sync也可以

        看成是一个AQS。

        还有一点要注意,即:acquireSharedInterruptibly传入的参数 “1” 在这里是没有意义的,可以

        不用管。

5.2、acquireSharedInterruptibly(int arg) 方法

         acquireSharedInterruptibly 方法功能是获取可中断的共享锁;在获取锁之前先判断当前线程

         是否已经被其他线程中断,若已经被中断,则抛出中断异常并退出。

         若当前线程没有被中断,则执行 tryAcquireShared() ,若 tryAcquireShared() 方法执行失败

         ,即 tryAcquireShared() 返回值小于0,则将当前线程放入AQS双向链表中阻塞。

                  acquireSharedInterruptibly 方法代码如下:

                     

                       

5.3、tryAcquireShared(int arg) 方法

         tryAcquireShared 方法字面上的含义是 尝试获取共享锁,但 tryAcquireShared 由各个子类

         实现,所以其在每个子类中含义也不一样。

         在 CountDownLatch.Sync 中 tryAcquireShared方法的功能是判断 CountDownLatch 计数器

         数值是否为0,若为0,则返回1,否则返回-1

          tryAcquireShared 代码如下所示:

                

5.4、countDown() 方法

        countDown() 方法没执行一次,CountDownLatch 计数器的值就会减1,直到计数器的值为

        0 为止。

        countDown() 方法中,把业务逻辑委托给内部类Sync方法 releaseShared 执行。

        countDown 方法代码如下:

              

5.5、releaseShared(int arg) 方法

        releaseShared 方法功能是释放共享锁,并唤醒后续阻塞的节点线程;

        releaseShared 方法代码如下:

              

       在 releaseShared 我们只需要关注方法 tryReleaseShared 的实现,其他方法的实现请参考

       前边的“AQS(二):共享锁的获取和释放”

5.6、tryReleaseShared(int arg)

         tryReleaseShared 方法的字面量含义是 尝试释放共享锁,但 tryReleaseShared 是一个默认

         方法,其由AQS各个子类实现,其在 CountDownLatch.Sync 中的实现功能是 将计数器的值

         减1,若计数器的值减1后为0,则返回true。

         tryReleaseShared 方法代码如下:

              

protected boolean tryReleaseShared(int releases) {// Decrement count; signal when transition to zero  递减计数;信号时过渡到零for (;;) {//获取当前的state值,代表完成整个操作数int c = getState();//c等于0,直接返回false//若c已经为0,表示计数器已经完成,当前线程是无效的if (c == 0)return false;//计算更新值,在 CountDownLatch 中表示计数器减1,有个现成执行完成int nextc = c-1;if (compareAndSetState(c, nextc))//CAS原子的修改state的值//CAS修改成功后,判断当前线程是否是最后一个完成的线程,即nextc是否等于0//若 nextc == 0 此时可能会有线程在await方法处挂起,则需要在countDown 处唤醒return nextc == 0;}}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Blender】快捷键_ 学习日志_01

【Blender】快捷键_ 学习日志_01 学习了blender的快捷键的使用。 2024年8月30日 视角操控 围绕中心旋转&#xff1a;按住鼠标中建 平移视角&#xff1a;Shift鼠标中键 视角前进后退&#xff1a;滚动滚轮 视图切换 0 切换到摄像机视图 1 切换正试图 2&#xff0c;4&…

产值8111亿!从《中国地理信息产业发展报告2024》看产业链上的就业蓝海

中国地理信息产业协会28日发布《中国地理信息产业发展报告2024》&#xff0c;报告显示2023年我国地理信息产业总产值达到8111亿元&#xff0c;同比增长4.2%&#xff0c;初步形成了完整的地理信息产业链。 《中国地理信息产业发展报告2024》显示&#xff0c;2023年以来&#xf…

倍福EL6751快速配置CANopen伺服

EL6751快速配置CANopen伺服 使用倍福EL6751快速配置方法&#xff0c;不要求提供从站的eds文件&#xff0c;但是需要提供从站的使用手册和通讯手册&#xff0c;用来查阅从站的PDO配置信息&#xff0c;这些配置参数会使用如下方法通过EL6751写入到从站中。 建立通用CANopen节点…

开源通用验证码识别OCR —— DdddOcr 源码赏析(二)

文章目录 前言DdddOcr分类识别调用识别功能classification 函数源码classification 函数源码解读1. 分类功能不支持目标检测2. 转换为Image对象3. 根据模型配置调整图片尺寸和色彩模式4. 图像数据转换为浮点数据并归一化5. 图像数据预处理6. 运行模型&#xff0c;返回预测结果 …

使用seamless-scroll-v3 实现无缝滚动,自动轮播平滑的滚动效果

安装&#xff1a;npm地址&#xff1a;https://www.npmjs.com/package/seamless-scroll-v3 yarn add seamless-scroll-v3# 或者使用 npm npm install seamless-scroll-v3# 或者使用 pnpm pnpm add seamless-scroll-v3 实现效果&#xff1a; template中的代码&#xff1a; <…

陷抄袭风波 《黑神话:悟空》该如何应对

都说人红是非多&#xff0c;国产首部3A游戏《黑神话&#xff1a;悟空》在爆火的同时&#xff0c;一些问题也随之出现。一方面《黑神话&#xff1a;悟空》陷入抄袭风波&#xff1f;另一方面该游戏也被很多黑灰产盯上了。 8月23日&#xff0c;“塞上李云中”发布微博&#xff0c;…

做为一名研发人员,你是如何看待项目管理软件这种产品的?

我认为项目管理软件是现代软件开发和项目管理中不可或缺的工具。它能够提高项目管理的效率和准确性&#xff0c;降低项目失败的风险&#xff0c;并为团队带来显著的价值。然而&#xff0c;在选择和使用项目管理软件时&#xff0c;团队需要综合考虑多个因素&#xff0c;以确保选…

hiprint打印/jsPDF使用/html2canvas

最初我知道hiprint.print是可以打印双模板的&#xff0c;于是查看hiprint.print的源码发现底层实现是this.getHtml(t).hiwprint,于是断点查看getHtm的实现&#xff0c;得知它是遍历我们对print传参的list&#xff0c;利用list中模板对象的getHtml()方法得到模板的dom对象&#…

如何使用电商API接口?(淘宝|京东商品详情数据接口)

一、了解电商API接口&#xff1a; 如今&#xff0c;在电商市场中&#xff0c;电商API接口的广泛应用极大地提高了电商行业的工作效率&#xff0c;使得商家能够灵活集成多种服务&#xff0c;高效优化业务流程。 当前&#xff0c;电商平台中的多种业务都可以通过使用API接口来做…

OpenGL/GLUT实践:水面模拟——从单振源到 Gerstner Wave(电子科技大学信软图形与动画Ⅱ实验)

源码见GitHub&#xff1a;A-UESTCer-s-Code 文章目录 1 实现效果1 简单水面模拟——单振源1.1 水面高度函数1.2 水面建模1.3 openGL 渲染(1) renderSense(2) 其他 1.4 实现效果 2 添加鼠标控制3 添加纹理4 多个振源组合5 Gerstner Wave 模型5.1 原理5.2 具体实现5.2.1 全局变量…

光伏气象分析包含哪些数据?

1.海拔 海拔是影响太阳辐射强度和气温的重要因素之一。高海拔地区通常大气稀薄&#xff0c;太阳辐射衰减较少&#xff0c;因此太阳辐射强度相对较高。同时&#xff0c;随着海拔的升高&#xff0c;气温和气压也会发生变化&#xff0c;这些变化对光伏组件的性能和发电效率有直接…

深度学习5从0到1理解RNN(包括LTSM,GRU等):内容丰富(下)

续 5.4.4 LSTM 举例 网络里面只有一个 LSTM 的单元&#xff0c;输入都是三维的向量&#xff0c;输出都是一维的输出。这三维的向量跟输出还有记忆元的关系是这样的。假设 x2 的值是 1 时&#xff0c; x1 的值就会被写到记忆元里&#xff1b;假设 x2 的值是-1 时&#xff0c;就…

计算机,数学,AI在社会模拟中的应用

这些模型通常属于社会模拟的范畴&#xff0c;利用计算机技术和复杂系统理论来模拟和预测社会动态。以下是几种常见的社会模拟模型&#xff1a; 1. 系统动力学模型 系统动力学模型通过建立数学方程来描述社会系统中的各种变量及其相互关系。这种模型适用于宏观层面的社会变化分…

uniapp 封装uni.login 实现全局调用

封装utils app.vue中 使用globalData 注册 utils 页面中使用方法 定义app 调用方法

ICM20948 DMP代码详解(1)

序言 接触Invensense的芯片这已经是第三次了。2015年在第二空间的时候第一次接触它的芯片&#xff0c;那时候是MPU9250&#xff1b;2021年的时候在智橙动力再一次接触到了MPU6050&#xff0c;那个时候用到了其中的DMP&#xff1b;这次接触的是ICM20948&#xff0c;按目前笔者理…

外接串口板,通过串口打开adb模式

一、依赖库 import subprocess import serial from serial.tools import list_ports import logging import time 二、代码 import subprocessimport serial from serial.tools import list_ports import logging import timedef openAdb(com):# com []# for i in list_por…

1、.Net UI框架:Avalonia UI - .Net宣传系列文章

Avalonia UI是一个开源的跨平台UI框架&#xff0c;它允许开发者使用C#和XAML来创建应用程序&#xff0c;这些应用程序可以在多个平台上运行&#xff0c;包括Windows、macOS、Linux、Android和iOS。Avalonia UI的设计目标是提供一个现代化、可移植的UI框架&#xff0c;它具有类似…

如何通过日志或gv$sql_audit,分析OceanBase运行时的异常SQL

本文作者&#xff1a;郑增权&#xff0c;爱可生 DBA 团队成员&#xff0c;OceanBase 和 MySQL 数据库技术爱好者。本文约 2000 字&#xff0c;预计阅读需要 8 分钟。 简介 在 OCP 云平台的 Top SQL 界面中&#xff0c;能观察到异常SQL&#xff0c;但这些SQL并未明确显示具体的…

上手一个RGBD深度相机:从原理到实践--ROS noetic+Astra S(上):解读深度测距原理和内外参推导

前言 最近在做项目的时候&#xff0c;项目组丢给了我一个深度相机&#xff0c;今天我们来尝试上手一个实体深度相机。 本教程设计基础相机的原理&#xff0c;使用&#xff0c;标定&#xff0c;和读取。(注&#xff1a;本教程默认大家有ROS1基础&#xff0c;故不对程序进行详细…

django外键表查询

Django外键&#xff08;ForeignKey&#xff09;操作以及related_name的作用-CSDN博客 django模型中外键操作_django的model的contain外键-CSDN博客 通过基本表可以查外键表 删基本表可以删外键表