Java并发类的主要API方法-CountDownLatch和CyclicBarrier

1.概念介绍

CountDownLatch 是一个计数器,计数器的初始值由创建它时指定。每次调用 countDown() 方法时,计数器会减1,直到计数器值变为0时,所有调用 await() 的线程都会被唤醒继续执行。

CyclicBarrier 是 Java 中另一个常用的同步辅助工具,它允许一组线程互相等待,直到所有线程都达到一个共同的屏障点(barrier)。与 CountDownLatch 不同的是,CyclicBarrier 可以被重用,即当所有线程都达到了屏障点并执行完相应任务后,CyclicBarrier 可以再次使用。CyclicBarrier 的主要优势在于它的重用性和灵活性,特别适用于循环性或分阶段的任务场景。

重用:CyclicBarrier 可以重用,而 CountDownLatch 是一次性的,计数器不能重置。

目的:CountDownLatch 用于让一个或多个线程等待其他线程完成,而 CyclicBarrier 是让一组线程互相等待,直到所有线程都到达一个屏障点。

2. 简单举例

1) CountDownLatch 简单示例:
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {int numberOfTasks = 3;CountDownLatch latch = new CountDownLatch(numberOfTasks);// 创建并启动任务for (int i = 0; i < numberOfTasks; i++) {new Thread(new Task(latch)).start();}// 主线程等待所有任务完成latch.await();System.out.println("所有任务已完成,继续主线程的操作。");}
}class Task implements Runnable {private CountDownLatch latch;public Task(CountDownLatch latch) {this.latch = latch;}@Overridepublic void run() {try {// 模拟任务执行时间Thread.sleep((long) (Math.random() * 1000));System.out.println(Thread.currentThread().getName() + " 任务完成");} catch (InterruptedException e) {e.printStackTrace();} finally {latch.countDown(); // 任务完成后计数器减1}}
}

解释

• CountDownLatch latch = new CountDownLatch(3);:初始化 CountDownLatch,计数器值为3。

• 每个线程在执行完任务后调用 latch.countDown();,计数器减1。

• latch.await(); 让主线程等待,直到计数器变为0,也就是所有任务完成。

CountDownLatch 非常适合用在需要等多个线程执行完毕后再执行下一步操作的场景,比如并行任务的同步。

2) CountDownLatch 有漏洞的比赛示例(有漏洞):

本实验想要实现的是裁判员要等待所有运动员各就各位后全部准备完毕,再开始比赛的效果。
创建测试用的项目代码如下:

package service;import java.util.concurrent.CountDownLatch;public class MyService {private CountDownLatch down = new CountDownLatch(1);public void testMethod() {try {System.out.println(Thread.currentThread().getName() + "准备");down.await();System.out.println(Thread.currentThread().getName() + "结束");} catch (InterruptedException e) {e.printStackTrace();}}public void downMethod() {System.out.println("开始");down.countDown();}}
线程类MyThread.java代码如下:
package extthread;import service.MyService;public class MyThread extends Thread {private MyService myService;public MyThread(MyService myService) {super();this.myService = myService;}@Overridepublic void run() {myService.testMethod();}}
运行类Run.java代码如下:
package test.run;import service.MyService;
import extthread.MyThread;public class Run {public static void main(String[] args) throws InterruptedException {MyService service = new MyService();MyThread[] tArray = new MyThread[10];for (int i = 0; i < tArray.length; i++) {tArray[i] = new MyThread(service);tArray[i].setName("线程" + (i + 1));tArray[i].start();}Thread.sleep(2000);service.downMethod();}
}
程序运行结果如下图所示:

此实验虽然运行成功,但并不能保证在main主线程中执行了service.downMethod();时,所有的工作线程都呈wait状态,因为某些线程有可能准备的时间花费较长,可能耗用的时间超过2秒,这时如果在第2秒时调用service.downMethod();方法就达不到“唤醒所有线程”继续向下运行的目的了,也就是说裁判员没有等全部的运动员到来时,就让发令枪响起开始比赛了,这是不对的,所以就需要对代码进行修改,来达到相对比较完善的比赛流程。

漏洞:countDown()方法的调用时机在所有任务完成任务的时间点之前,导致问题。

改进:等待10个线程要用CountDownLatch(10), 或者用CyclicBarrier。

2) CountDownLatch 完整的比赛示例:

使用CountDownLatch类来实现“所有的线程”呈wait后再统一唤醒的效果,通过大量使用CountDownLatch类来实现业务要求的同步效果。
创建实验用的项目,代码如下:

package extthread;import java.util.concurrent.CountDownLatch;public class MyThread extends Thread {private CountDownLatch comingTag;   // 裁判等待所有运动员到来private CountDownLatch waitTag;        // 等待裁判说准备开始private CountDownLatch waitRunTag;  // 等待起跑private CountDownLatch beginTag;      // 起跑private CountDownLatch endTag;        // 所有运动员到达终点public MyThread(CountDownLatch comingTag, CountDownLatch waitTag,CountDownLatch waitRunTag, CountDownLatch beginTag,CountDownLatch endTag) {super();this.comingTag = comingTag;this.waitTag = waitTag;this.waitRunTag = waitRunTag;this.beginTag = beginTag;this.endTag = endTag;}@Overridepublic void run() {try {System.out.println("运动员使用不同交通工具不同速度到达起跑点,正向这头走!");Thread.sleep((int) (Math.random() * 10000));System.out.println(Thread.currentThread().getName() + "到起跑点了!");comingTag.countDown();System.out.println("等待裁判说准备!");waitTag.await();System.out.println("各就各位!准备起跑姿势!");Thread.sleep((int) (Math.random() * 10000));waitRunTag.countDown();beginTag.await();System.out.println(Thread.currentThread().getName()+ " 运行员起跑 并且跑赛过程用时不确定");Thread.sleep((int) (Math.random() * 10000));endTag.countDown();System.out.println(Thread.currentThread().getName() + " 运行员到达终点");} catch (InterruptedException e) {e.printStackTrace();}}}
运行类Run.java代码如下:
package test;import java.util.concurrent.CountDownLatch;
import extthread.MyThread;public class Run {public static void main(String[] args) {try {CountDownLatch comingTag = new CountDownLatch(10);CountDownLatch waitTag = new CountDownLatch(1);CountDownLatch waitRunTag = new CountDownLatch(10);CountDownLatch beginTag = new CountDownLatch(1);CountDownLatch endTag = new CountDownLatch(10);MyThread[] threadArray = new MyThread[10];for (int i = 0; i < threadArray.length; i++) {threadArray[i] = new MyThread(comingTag, waitTag, waitRunTag,beginTag, endTag);threadArray[i].start();}System.out.println("裁判员在等待选手的到来!");comingTag.await();System.out.println("裁判看到所有运动员来了,各就各位前“巡视”用时5秒");Thread.sleep(5000);waitTag.countDown();System.out.println("各就各位!");waitRunTag.await();Thread.sleep(2000);System.out.println("发令枪响起!");beginTag.countDown();endTag.await();System.out.println("所有运动员到达,统计比赛名次!");} catch (InterruptedException e) {e.printStackTrace();}}}

运动员使用不同交通工具不同速度到达起跑点,正向这头走!
运动员使用不同交通工具不同速度到达起跑点,正向这头走!
运动员使用不同交通工具不同速度到达起跑点,正向这头走!
运动员使用不同交通工具不同速度到达起跑点,正向这头走!
运动员使用不同交通工具不同速度到达起跑点,正向这头走!
运动员使用不同交通工具不同速度到达起跑点,正向这头走!
运动员使用不同交通工具不同速度到达起跑点,正向这头走!
运动员使用不同交通工具不同速度到达起跑点,正向这头走!
运动员使用不同交通工具不同速度到达起跑点,正向这头走!
裁判员在等待选手的到来!
运动员使用不同交通工具不同速度到达起跑点,正向这头走!
Thread-9到起跑点了!
等待裁判说准备!
Thread-2到起跑点了!
等待裁判说准备!
Thread-7到起跑点了!
等待裁判说准备!
Thread-5到起跑点了!
等待裁判说准备!
Thread-4到起跑点了!
等待裁判说准备!
Thread-0到起跑点了!
等待裁判说准备!
Thread-1到起跑点了!
等待裁判说准备!
Thread-3到起跑点了!
等待裁判说准备!
Thread-8到起跑点了!
等待裁判说准备!
Thread-6到起跑点了!
等待裁判说准备!
裁判看到所有运动员来了,各就各位前“巡视”用时5秒
各就各位!
各就各位!准备起跑姿势!
各就各位!准备起跑姿势!
各就各位!准备起跑姿势!
各就各位!准备起跑姿势!
各就各位!准备起跑姿势!
各就各位!准备起跑姿势!
各就各位!准备起跑姿势!
各就各位!准备起跑姿势!
各就各位!准备起跑姿势!
各就各位!准备起跑姿势!
发令枪响起!
Thread-9 运行员起跑 并且跑赛过程用时不确定
Thread-1 运行员起跑 并且跑赛过程用时不确定
Thread-4 运行员起跑 并且跑赛过程用时不确定
Thread-7 运行员起跑 并且跑赛过程用时不确定
Thread-8 运行员起跑 并且跑赛过程用时不确定
Thread-5 运行员起跑 并且跑赛过程用时不确定
Thread-0 运行员起跑 并且跑赛过程用时不确定
Thread-2 运行员起跑 并且跑赛过程用时不确定
Thread-3 运行员起跑 并且跑赛过程用时不确定
Thread-6 运行员起跑 并且跑赛过程用时不确定
Thread-2 运行员到达终点
Thread-8 运行员到达终点
Thread-4 运行员到达终点
Thread-5 运行员到达终点
Thread-9 运行员到达终点
Thread-7 运行员到达终点
Thread-6 运行员到达终点
Thread-3 运行员到达终点
Thread-0 运行员到达终点
Thread-1 运行员到达终点
所有运动员到达,统计比赛名次!
4) CyclicBarrier 简单示例:

每次当所有线程都到达屏障点(即调用 await()),屏障就会被“破坏”,所有等待的线程继续执行,CyclicBarrier 会自动重置为初始状态,允许它再次被使用。这个过程可以无限次重复。

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;public class CyclicBarrierReuseExample {public static void main(String[] args) {int numberOfThreads = 3;CyclicBarrier barrier = new CyclicBarrier(numberOfThreads, new Runnable() {@Overridepublic void run() {System.out.println("所有线程都已到达屏障点,继续下一阶段...");}});// 启动3个线程,它们将经过5个阶段,每个阶段有3个屏障点for (int i = 0; i < numberOfThreads; i++) {new Thread(new Task(barrier)).start();}}
}class Task implements Runnable {private CyclicBarrier barrier;public Task(CyclicBarrier barrier) {this.barrier = barrier;}@Overridepublic void run() {try {for (int i = 0; i < 5; i++) { // 让线程经过5个阶段,模拟5个阶段System.out.println(Thread.currentThread().getName() + " 正在执行阶段 " + (i + 1) + "...");Thread.sleep((long) (Math.random() * 1000)); // 模拟任务处理时间System.out.println(Thread.currentThread().getName() + " 到达屏障点 " + (i + 1));barrier.await(); // 等待其他线程到达屏障点// 继续执行下一阶段}} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}
}
Thread-0 正在执行阶段 1...
Thread-1 正在执行阶段 1...
Thread-2 正在执行阶段 1...
Thread-0 到达屏障点 1
Thread-1 到达屏障点 1
Thread-2 到达屏障点 1
所有线程都已到达屏障点,继续下一阶段...
Thread-2 正在执行阶段 2...
Thread-1 正在执行阶段 2...
Thread-0 正在执行阶段 2...
...
所有线程都已到达屏障点,继续下一阶段...
Thread-0 正在执行阶段 5...
Thread-1 正在执行阶段 5...
Thread-2 正在执行阶段 5...
Thread-2 到达屏障点 5
Thread-0 到达屏障点 5
Thread-1 到达屏障点 5
所有线程都已到达屏障点,继续下一阶段...

解释:

重用:for (int i = 0; i < 5; i++) 循环中,每个线程都会经过5次屏障点。每当所有线程都到达屏障点并通过时,CyclicBarrier 会自动重置,使得它可以在下一轮循环中继续使用。

屏障点:在每个阶段,所有线程都需要等到其他线程都到达相同的屏障点,然后再继续执行。这模拟了多阶段任务中同步的场景。

线程同步:在每个屏障点,线程都调用 barrier.await() 来等待其他线程,直到所有线程都到达,这时屏障才会被解除,线程们才继续执行。

自动重置:在每个阶段的屏障点解除后,CyclicBarrier 自动重置,以便在下一个阶段再次使用。

通过这个例子可以看出,CyclicBarrier 的重用特性非常适合于那些需要分阶段执行的任务,确保每个阶段都在所有线程同步完成后再开始下一个阶段。

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

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

相关文章

渗透率超50%,新能源汽车已成中国制造新名片

近日&#xff0c;乘联会公布了最新一期全国乘用车市场分析报告。报告显示&#xff0c;今年7月&#xff0c;中国乘用车市场零售销量173.2万辆&#xff0c;批发销量195.6万辆&#xff1b;同期&#xff0c;新能源乘用车零售销量87.8万辆&#xff0c;批发销量94.5万辆。按此计算&am…

DC-4靶机渗透测试

一、靶机下载地址 https://www.vulnhub.com/entry/dc-4,313/ 二、信息收集 1、主机发现 # 使用命令 nmap 192.168.145.0/24 -sn | grep -B 2 "00:0C:29:43:49:A5" 2、端口扫描 # 使用命令 nmap 192.168.145.217 -p- -sV 3、指纹识别 # 使用命令 whatweb "…

顺序表各种接口的实现(C)

线性表 线性表是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串…线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直线。在物理结构上并不一定是连续的&#xff0c;线性表在物…

旋转字符串 | LeetCode-796 | 模拟 | KMP | 字符串匹配

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f579;️KMP算法练习题 LeetCode链接&#xff1a;796. 旋转字符串 文章目录 1.题目描述&#x1f351;2.题解&#x1fad0;2.1 暴力解法&#x1fad2;2.2 模拟…

c# 排序、强转枚举

List<Tuple<double,int>> mm中doble从小到大排序 mm本身排序 在C#中&#xff0c;如果你有一个List<Tuple<double, int>>类型的集合mm&#xff0c;并且你想要根据Tuple中的double值&#xff08;即第一个元素&#xff09;从小到大进行排序&#xff0c;同…

[Qt][对话框][下]详细讲解

目录 1.Qt内置对话框0.有哪些1.消息对话框 QMessageBox2.颜色对话框 QColorDialog3.⽂件对话框 QFileDialog4.字体对话框 QFontDialog5.输⼊对话框 QInputDialog6.进度条对话框 QProgressDialog 1.Qt内置对话框 0.有哪些 Qt提供了多种可复⽤的对话框类型&#xff0c;即Qt标准…

【启明智显技术分享】工业级HMI芯片Model3C/Model3A开发过程中问题记录笔记二

一、Model3C/Model3A芯片介绍 Model3C/Model3A是启明智显针对工业、行业以及车载产品市场推出的一款高性能、低成本的工业级HMI&#xff08;Human-Machine Interface&#xff0c;人机界面&#xff09;芯片。两颗芯片硬件PIN TO PIN&#xff1b;区别在于内置的PSRAM大小不同。该…

百度地图动态绘制台风轨迹

效果图如下: 台风测试数据获取 关键代码: /*** 动态绘制点和线*/drawMakerByAnimate () {const pointsMap = typhoneData.points;const title = typhoneData.tfid + typhoneData.name;if (!pointsMap || pointsMap.length === 0) {return;}if (this.markers.length > 0 &…

Godot《躲避小兵》实战之设置项目

通过之前的学习我们已经基本了解了godot的界面&#xff0c;知道如何创建项目以及节点。那么&#xff0c;从这一章节我们将进入godot官方给我们提供的一个2D游戏开发的小教程进行入手&#xff0c;这个游戏并不是我自己的作品&#xff0c;而是我通过学习完之后&#xff0c;对其进…

C#如何将自己封装的nuget包引入到项目中

问题 自己封装好了一个nuget包&#xff0c;但是不想上传到外网&#xff0c;想局域网使用&#xff0c;有两种方案 搭建私有nuget仓库放到离线文件夹中直接使用 第一种方式请请参考proget安装 下面主要是第二种方式 准备 新建类库项目 using System;namespace ClassLibrary…

数据结构--图(Graph)

定义 图&#xff08;Graph&#xff09;是由顶点的有穷非空集合和顶点之间边的集合组成的一种非线性表结构&#xff0c;通常表示为&#xff1a;G(V,E)&#xff0c;其中&#xff0c;G表示一个图&#xff0c;V是图G中顶点的集合&#xff0c;E是图G中边的集合。 顶点&#xff08;…

阿里云智能大数据演进

本文根据7月24日飞天发布时刻产品发布会、7月5日DataFunCon2024北京站&#xff1a;大数据大模型.双核时代实录整理而成&#xff0c;演讲信息如下&#xff1a; 演讲人&#xff1a;徐晟 阿里云研究员/计算平台产品负责人 主要内容&#xff1a; Overview - 阿里云大数据 AI 产品…

经典算法题总结:数组常用技巧(双指针,二分查找和位运算)篇

双指针 在处理数组和链表相关问题时&#xff0c;双指针技巧是经常用到的&#xff0c;双指针技巧主要分为两类&#xff1a;左右指针和快慢指针。所谓左右指针&#xff0c;就是两个指针相向而行或者相背而行&#xff1b;而所谓快慢指针&#xff0c;就是两个指针同向而行&#xf…

【YOLO5 项目实战】(3)PCB 缺陷检测

欢迎关注『youcans动手学模型』系列 本专栏内容和资源同步到 GitHub/youcans 【YOLO5 项目实战】(1)YOLO5 环境配置与测试 【YOLO5 项目实战】(2)使用自己的数据集训练目标检测模型 【YOLO5 项目实战】(3)PCB 缺陷检测 【YOLO5 项目实战】(3)PCB 缺陷检测 1. PCB 缺陷检…

vue-cli 中 配置 productionSourceMap 为 false 失效?

背景 最近 发现 vuecli 构建的 项目中配置的 productionSourceMap 为 false 后 &#xff0c;生产代码 还是能够看到 sourceMap 文件 。 原因 生效前提条件 得设置 NODE_ENV 为 production 才会生效&#xff01; 解决 直接修改生产环境的配置 NODE_ENV 为 production 直接覆…

融资3亿美元——月之暗面:AI大模型领域的新星

月之暗面&#xff0c;这个名字在AI领域引起了不小的震动。作为国内大模型创业企业的佼佼者&#xff0c;月之暗面以其独特的技术优势和商业模式&#xff0c;迅速在激烈的市场竞争中崭露头角。同时以其出色的长文本处理能力和创新的商业模式吸引了众多投资者的目光。 优牛企讯-企…

基于DETR模型实现交通标志检测

交通标志检测在自动驾驶和交通监控中是一个重要的问题。目标检测技术极大地促进了这一过程的自动化。本文实现基于DETR目标检测模型识别交通标志。 Tiny LISA交通标志检测数据集 本文数据集使用Kaggle上提供的Tiny LISA交通标志检测数据集(https://www.kaggle.com/datasets/mm…

手把手教你CNVD漏洞挖掘 + 资产收集

0x1 前言 挖掘CNVD漏洞有时候其实比一般的edusrc还好挖&#xff0c;但是一般要挖证书的话&#xff0c;还是需要花时间的&#xff0c;其中信息收集&#xff0c;公司资产确定等操作需要花费一定时间的。下面就记录下我之前跟一个师傅学习的一个垂直越权成功的CNVD漏洞通杀&#…

MySql 5.7.1 分区的实践

在性能优化中&#xff0c;Mysql 进行分区&#xff0c;能有效提高查询效率&#xff0c;因此开始百度了起来。但是结果总不是那么一番风顺的。如今使用 uuid 作为主键的情况已是主流&#xff0c;因此在给表增加分区时&#xff0c;出现了如下错误&#xff1a; 错误&#xff1a; A …

AI在医学领域:联邦学习 (FL) 在肿瘤学的应用综述

关键词&#xff1a;联邦学习 (Federated Learning, FL)、机器学习 (Machine Learning, ML)、肿瘤学 (Oncology)、数据隐私 (Data Privacy)、精准医疗 (Precision Medicine)、多模态 (Multi-modal) 肿瘤学正在经历快速的变革&#xff0c;这得益于机器学习&#xff08;ML&#xf…