并发编程 | CountDownLatch是如何控制线程执行流程

news2024/9/23 23:29:20

CountDownLatch 是 Java 并发编程中的一个同步工具类,这个工具经常用来用来协调多个线程之间的同步,下面我们就来一起认识一下 CountDownLatch。

CountDownLatch介绍

应用场景

CountDownLatch主要是用于让一个或多个线程等待其他线程完成某些操作后再继续执行。例如,小组早上开会,组长(主线程)只有等所有人(每个人代表一个线程)到达会议室才能开;再如,游乐园里的过山车,一次可以坐10个人,为了节约成本,通常是等够10个人了才开。

CountDownLatch的核心思想是“倒计时”:线程在执行之前,需要等到计数器倒数到零。所以CountDownLatch的用法通常是设定一个大于0的值,该值即代表需要等待的总任务数,每完成一个任务后,将总任务数减一,直到最后该值为0,说明所有等待的任务都执行完了,后面的任务可以继续执行。

核心方法

CountDownLatch类的核心方法有以下几个。

构造函数:CountDownLatch的构造函数需要传入一个int类型参数count,该参数表示需要倒数的值。

public CountDownLatch(int count)
...
}

await():调用 await() 方法的线程开始阻塞,直到倒计时结束,也就是 count 值为 0 的时候才会继续执行。await方法还有一个版本await(long timeout, TimeUnit unit),这个版本可以设置超时时间,如果超时就不再等待了。

countDown():每当一个线程完成了某个操作,它就调用 countDown() 方法将计数器减一,如果减到 0 时,之前等待的线程会被唤起。

public void countDown()

public boolean await(long timeout, TimeUnit unit) throws InterruptedException

CountDownLatch用法

一个线程等待其他多个线程都执行完毕

举个生活中的例子,张老师是一个对学生非常严格的老师,在她的班级里,只有所有学生当天的作业都完成了,张老师才会放学,我们就可以用CountDownLatch来模拟这个场景,为了简单起见,假设班里只有3个学生,代码如下:

class DoHomeworkRunnable implements Runnable{
    CountDownLatch countDownLatch = null;
    private int i;

    public DoHomeworkRunnable(CountDownLatch countDownLatch, int i) {
        this.countDownLatch = countDownLatch;
        this.i = i;
    }

    @Override
    public void run() {
        try {
            Thread.sleep((i+1)*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println((i+1) + "号同学完成了作业");
        countDownLatch.countDown();
    }
}

//一个线程等待其他多个线程都执行完毕,再继续自己的工作
public class CountDownLatchExample1 {
    public static void main(String[] args) throws InterruptedException {
        long startTime = Calendar.getInstance().getTimeInMillis();
        CountDownLatch countDownLatch = new CountDownLatch(3);
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (int i=0; i<3; i++){
            executorService.submit(new DoHomeworkRunnable(countDownLatch, i));
        }

        System.out.println("等待所有同学都完成 作业");
        countDownLatch.await();
        System.out.println("所有同学都完成作业,放学");

        long endTime = Calendar.getInstance().getTimeInMillis();
        System.out.println("程序执行了  " + (endTime -  startTime) + " ms");
    }
}

//输出
等待所有同学都完成 作业
1号同学完成了作业
2号同学完成了作业
3号同学完成了作业
所有同学都完成作业,放学
程序执行了  3018 ms

在这段代码中,我们新建了一个初始值为3的CountDownLatch,然后建立了线程数为3的线程池,往线程池中提交3个任务,每个任务代表一个学生,这个学生会sleep一段时间,模拟写作业的过程,然后打印出他完成了作业,在每个学生完成作业之后,都会调用 countDown 方法来把计数减 1。

主线程中会调用 await() 方法,在count值变为 0之前会让主线程等待,直到几个子线程都执行完毕都 完成了作业后,它才会继续执行打印出 “所有同学都完成作业,放学”。为什么程序执行了3s左右,这是因为这3个任务中,3号学生执行了3s。

可以用下面的流程图帮助我们理解:

多个线程等待某一个线程的信号

这个用法和上面的用法有点相反,还是拿老师和同学的场景举例:打完上课铃之后,老师会等待同学们5s,等待同学都进入教室,然后喊起立,只有老师喊完起立后,同学们才会同时站起来。代码如下:


class ReadyRunnable implements Runnable{
    CountDownLatch countDownLatch = null;
    private int i;

    public ReadyRunnable(CountDownLatch countDownLatch, int i) {
        this.countDownLatch = countDownLatch;
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println((i+1) + "号同学进入教室 ,等待老师喊起立");
        try {
            countDownLatch.await();
            System.out.println((i+1) + "号同学站起");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//多个线程等待某一个线程的信号,同时开始执行
public class CountDownLatchExample2 {
    public static void main(String[] args) throws InterruptedException {
        long startTime = Calendar.getInstance().getTimeInMillis();
        CountDownLatch countDownLatch = new CountDownLatch(1);
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (int i=0; i<3; i++){
            executorService.submit(new ReadyRunnable(countDownLatch, i));
        }

        Thread.sleep(5000);
        countDownLatch.countDown();
        System.out.println("5秒准备时间已过,老师喊起立!");
        long endTime = Calendar.getInstance().getTimeInMillis();
        System.out.println("程序执行了  " + (endTime -  startTime) + " ms");
    }
}

//输出
1号同学进入教室 ,等待老师喊起立
3号同学进入教室 ,等待老师喊起立
2号同学进入教室 ,等待老师喊起立
5秒准备时间已过,老师喊起立!
1号同学站起
3号同学站起
2号同学站起
程序执行了  5020 ms

在这段代码中,新建了一个 CountDownLatch,其倒数值只有 1;然后建立了线程数为3的线程池,往线程池中提交3个任务,而这3个任务在一开始时就让它调用 await() 方法开始等待。

主线程sleep5 秒,等待同学们进入教室,5 秒之后,主线程调用countDown()将倒数值减为0,然后老师喊起立,此时,之前那 3 个已经调用了 await() 方法的子线程都会被唤醒执行,同时打印出"x号同学站起"。

同样画了一张流程图帮助理解:

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

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

相关文章

鸿蒙Harmony实战开发知识:“UIAbility组件的3种启动模式”

UIAbility的启动模式是指UIAbility实例在启动时的不同呈现状态。针对不同的业务场景&#xff0c;系统提供了三种启动模式&#xff1a; singleton启动模式 singleton启动模式为单实例模式&#xff0c;也是默认情况下的启动模式。 每次调用startAbility()方法时&#xff0c;如…

windows下的redis7.0.11的下载

天&#xff0c;我找redis7.0.11的安装包就找了好久&#xff0c;终于给我找到了。市面上好多是linux版本的。 安装包&#xff1a;Release Redis 7.0.11 for Windows zkteco-home/redis-windows GitHub 解压之后是这样的。 然后你要测试能不能启动&#xff1a; 1、指定配置文…

复现DOM型XSS攻击(1-8关)

目录 第一关&#xff1a;​ 分析代码&#xff1a; 第二关&#xff1a; 分析代码&#xff1a; 第三关&#xff1a; 分析代码&#xff1a; 第四关&#xff1a; 分析代码&#xff1a; 第五关&#xff1a; 分析代码&#xff1a; 第六关&#xff1a; 分析代码&#xff1…

volatitle-线程并发-小白一文速通

目录 简而言之 专业术语解释 1、可见性 原理简介 原理图解 其他方式 2、原子性 原理简介 结合实例分析 3、有序性 原理简介 线程安全问题 Volatile效果 1、保证可见性 2、保证有序性 3、无法保证原子性 Volatile底层的实现机制(重点掌握) 经典案例 Java双重检…

朗致面试----Java开发、Java架构师

一共三轮面试。第一轮是逻辑行测&#xff0c;第二轮是技术面试&#xff08;面试官-刘老师&#xff09;&#xff0c;第三轮是CTO面试&#xff08;面试官-屠老师&#xff09;。第三轮Coding做完之后共享屏幕讲一个你自己负责过的项目&#xff08;请提前准备好架构图&#xff0c;开…

【AI趋势8】具身智能

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已经从概念走向实际应用&#xff0c;深刻影响着我们的生产和生活方式。在众多AI技术载体中&#xff0c;人型机器人凭借其独特的类人形态和全身自由度&#xff0c;成为人工智能领域的终极载体之一。本文将深入…

系列:水果甜度个人手持设备检测-产品规划汇总与小结

系列:水果甜度个人手持设备检测 -- 产品规划汇总与小结 背景 接上一篇&#xff0c;我们从假设的用户需求出发&#xff0c;规划输出软硬件结合的一体化产品。在产品中搭载近红外光谱&#xff08;NIR&#xff09;、超声检测的模块&#xff0c;并针对不同的瓜果类型&#xff0c…

【开源分享】CommLite 跨平台文本UI串口调试助手

文章目录 1. 简介2. 编译3. 使用4. 借鉴&思考参考 1. 简介 CommLite是一款基于CSerialPort的文本UI串口调试助手。 gitee仓库 2. 编译 编译非常简单&#xff0c;按照文档操作即可&#xff1a; $ git clone --depth1 https://github.com/itas109/CommLite.git $ cd Comm…

数据结构----AVL树

小编会一直更新数据结构相关方面的知识&#xff0c;使用的语言是Java&#xff0c;但是其中的逻辑和思路并不影响&#xff0c;如果感兴趣可以关注合集。 希望大家看完之后可以自己去手敲实现一遍&#xff0c;同时在最后我也列出一些基本和经典的题目&#xff0c;可以尝试做一下。…

牛客网习题——通过C++实现

一、目标 实现下面4道练习题增强C代码能力。 1.求123...n_牛客题霸_牛客网 (nowcoder.com) 2.计算日期到天数转换_牛客题霸_牛客网 (nowcoder.com) 3.日期差值_牛客题霸_牛客网 (nowcoder.com) 4.打印日期_牛客题霸_牛客网 (nowcoder.com) 二、对目标的实现 1.求123...n_…

Java二十三种设计模式-访问者模式(21/23)

本文深入探讨了访问者模式&#xff0c;一种允许向对象结构添加新操作而不修改其本身的设计模式&#xff0c;涵盖了其定义、组成部分、实现方式、使用场景、优缺点、与其他模式的比较&#xff0c;以及最佳实践和替代方案。 访问者模式&#xff1a;为对象结构添加新的操作 引言 …

黑神话:悟空-配置推荐

显卡推荐&#xff08;按类别整理&#xff09; 1. GTX 10系列、GTX 16系列&#xff1a; 如果希望体验光线追踪&#xff0c;建议根据预算升级到RTX 40系列显卡。对于1080p分辨率&#xff0c;至少需要RTX 4060才能流畅运行。 2. RTX 20系列&#xff1a; RTX 2060、RTX 2070&#…

Openboxes 移动终端APP项目开发环境搭建与调试

文章目录 前言项目简介APP开发环境搭建APP开发环境启动及调试主应用程序启动及调试结语 前言 openboxes 项目还有一个针对移动端的项目&#xff1a;openboxes-mobile&#xff0c;但是这个项目的默认分支&#xff08;develop&#xff09;并没有与openboxes的默认分支对应&#…

LabVIEW优化内存使用

在LabVIEW中&#xff0c;优化内存使用的关键在于理解LabVIEW的内存管理机制并采用一些最佳实践。以下是一些可能帮助减少内存占用的方法&#xff1a; 1. 减少数据副本的生成 避免不必要的数据复制&#xff1a;每当你在程序中传递数组或子数组时&#xff0c;LabVIEW可能会创建副…

充电宝哪些品牌的性价比是最高的?开学最推荐入手四款充电宝

随着新学期的到来&#xff0c;学生们对充电宝的需求愈发迫切。无论是在校园内上课、图书馆自习&#xff0c;还是在外出游玩时&#xff0c;充电宝都成为了我们必不可少的随身装备。然而&#xff0c;市场上充斥着各种品牌和型号&#xff0c;如何选择一款性价比高的充电宝&#xf…

React+Vis.js(04):设置节点显示图片

文章目录 实现效果关键代码完整代码设置图片边框和背景颜色我们继续以 复仇者联盟为例,来介绍如何实现节点显示 图片。 实现效果 以图片进行节点的显示,使得显示效果更加直观,信息更为明了。 关键代码 在vis.js中,通过属性shape来控制节点显示为图像。 const nodes …

linux | 苹果OpenCL(提高应用软件如游戏、娱乐以及科研和医疗软件的运行速度和响应)

点击上方"蓝字"关注我们 01、引言 >>> OpenCL 1.0 于 2008 年 11 月发布。 OpenCL 是为个人电脑、服务器、移动设备以及嵌入式设备的多核系统提供并行编程开发的底层 API。OpenCL 的编程语言类似于 C 语言。其可以用于包含 CPU、GPU 以及来自主流制造商如 …

关于Hipe并发库中动态线程库DynamicThreadPond的一点解读(二)

文章目录 前提动态减少代码解读 动态增加线程池退出时发生了什么&#xff1f;总结附录 前提 我们在关于Hipe并发库中动态线程库DynamicThreadPond的一点解读(一)中介绍了DynamicThreadPond如何初始化&#xff0c;如何向任务队列中添加任务&#xff0c;线程池中的线程如何执行任…

机器学习——第十二章 计算学习理论

目录 12.1 基础知识 12.2 PAC学习 12.3 有限假设空间 12.3.1 可分情形 12.3.2 不可分情形 12.4 VC维 12.5 Rademacher复杂度 12.6 稳定性 12.1 基础知识 计算学习理论(computational learning theory)研究的是关于通过"计算"来进行"学习"的理论…

SAP无参考收货

其他收货 如果我们未参考其他凭证&#xff08;采购订单、生产订单或预留&#xff09;输入货物移动&#xff0c;则我们将讲到其他收货。因为在实际过帐之前&#xff0c;系统不会存储任何物料、数量、交货日期、接收工厂或来源方面的信息&#xff0c;所以此类收货属于计划外货物…