关于CountDownLatch

news2024/11/18 0:31:07

关于CountDownLatch

  • CountDownLatch 是什么
  • CountDownLatch 如何工作
  • CountDownLatch API
  • 使用示例
  • 与 Join 的区别

CountDownLatch 是什么

CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。

使用场景:
Zookeeper分布式锁,Jmeter模拟高并发等。

CountDownLatch 如何工作

CountDownLatch 是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后调用 countDown(),计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,被 await() 方法阻塞的线程才会被释放。

CountDownLatch API

CountDownLatch API 描述

使用示例

假设一条流水线上有三个工作者:worker0,worker1,worker2。有一个任务的完成需要他们三者协作完成,worker2可以开始这个任务的前提是 worker0 和 worker1 完成了他们的工作,而 worker0 和worker1 是可以并行他们各自的工作的。

接下来我们创建一个计数器为 2 的 CountDownLatch 实例 ,让Worker 持有这个 CountDownLatch 实例,当完成自己的工作后,调用countDownLatch. countDown() 方法将计数器减1。countDownLatch.await() 方法会一直阻塞直到计数器为0,主线程才会继续往下执行。

Worker 类如下:

package com.concurrent.test4;
 
import java.util.concurrent.CountDownLatch;
 
/**
 * 工作者类
 * @author ThinkPad
 *
 */
public class Worker extends Thread {
 
	//工作者名
        private String name;
	//工作时间
	private long time;
	
	private CountDownLatch countDownLatch;
	
	public Worker(String name, long time, CountDownLatch countDownLatch) {
		this.name = name;
		this.time = time;
		this.countDownLatch = countDownLatch;
	}
	
	@Override
	public void run() {
		try {
			System.out.println(name+"开始工作");
			Thread.sleep(time);
			System.out.println(name+"工作完成,耗费时间="+time);
			countDownLatch.countDown();
			System.out.println("countDownLatch.getCount()="+countDownLatch.getCount());
		} catch (InterruptedException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}	
	}
}

Test 类如下:

package com.concurrent.test4;
 
import java.util.concurrent.CountDownLatch;
 
 
public class Test {
 
	public static void main(String[] args) throws InterruptedException {
		CountDownLatch countDownLatch = new CountDownLatch(2);
		Worker worker0 = new Worker("worker0", (long) (Math.random()*2000+3000), countDownLatch);
		Worker worker1 = new Worker("worker1", (long) (Math.random()*2000+3000), countDownLatch);
		Worker worker2 = new Worker("worker2", (long) (Math.random()*2000+3000), countDownLatch);
		
		worker0.start();
		worker1.start();
		
		countDownLatch.await();
		System.out.println("准备工作就绪");
		worker2.start();		
	}
}

观察运行结果:

worker1开始工作
worker0开始工作
worker0工作完成,耗费时间=3174
countDownLatch.getCount()=1
worker1工作完成,耗费时间=3870
countDownLatch.getCount()=0
准备工作就绪
worker2开始工作
worker2工作完成,耗费时间=3992

与 Join 的区别

除了 CountDownLatch 外,我们还可以使用 Join 来模拟以上场景。

当在当前线程中调用某个线程 thread 的 join() 方法时,当前线程就会阻塞,直到 thread 执行完成,当前线程才可以继续往下执行。

补充下:join 的工作原理是,不停检查 thread 是否存活,如果存活则让当前线程永远 wait,直到 thread 线程终止,线程的 this.notifyAll() 就会被调用。

接下来使用 Join 来实现一下。

Worker 类如下:

package com.concurrent.test3;
 
/**
 * 工作者类
 * @author ThinkPad
 *
 */
public class Worker extends Thread {
 
	//工作者名
	private String name;
	//工作时间
	private long time;
	
	public Worker(String name, long time) {
		this.name = name;
		this.time = time;
	}
	
	@Override
	public void run() {
		// TODO 自动生成的方法存根
		try {
			System.out.println(name+"开始工作");
			Thread.sleep(time);
			System.out.println(name+"工作完成,耗费时间="+time);
		} catch (InterruptedException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}	
	}
}

Test 类如下:

package com.concurrent.test3;
 
 
public class Test {
 
	public static void main(String[] args) throws InterruptedException {
 
		Worker worker0 = new Worker("worker0", (long) (Math.random()*2000+3000));
		Worker worker1 = new Worker("worker1", (long) (Math.random()*2000+3000));
		Worker worker2 = new Worker("worker2", (long) (Math.random()*2000+3000));
		
		worker0.start();
		worker1.start();
		
		worker0.join();
		worker1.join();
		System.out.println("准备工作就绪");
		
		worker2.start();		
	}
}

观察运行结果:

worker1开始工作
worker0开始工作
worker1工作完成,耗费时间=3947
worker0工作完成,耗费时间=4738
准备工作就绪
worker2开始工作
worker2工作完成,耗费时间=4513

可以发现 Join 确实可以完成这个需求,那么它和 CountDownLatch 有哪些区别呢?

目前所知的 CountDownLatch 与 Join 的不同点:

Join 会不停检查 thread 是否存活,如果存活则让当前线程永远 wait,直到 thread 线程终止,线程的 this.notifyAll() 就会被调用。

CountDownLatch 通过计数器是否为 0 决定是否释放资源,并不需要线程终止才会去释放资源。

可见 CountDownLatch 比 Join 要灵活许多,我们使用另一种场景来说明这个问题。

假设 worker 的工作可以分为两个阶段,work2 只需要等待 work0和 work1 完成他们各自工作的第一个阶段之后就可以开始自己的工作了,而不是场景1中的必须等待 work0 和 work1 把他们的工作全部完成之后才能开始。

这种情况是 Join 无法实现的,而 CountDownLatch 却很轻松。我们可以在 worker0 和 worker1 完成第一阶段工作之后就 调用 countDown() 把计数器减 1 即可,这样 worker0 和 worker1 在完成第一阶段工作之后,worker2 就可以开始工作了。

Worker 类如下:

package com.concurrent.test5;
 
import java.util.concurrent.CountDownLatch;
 
/**
 * 工作者类
 * @author ThinkPad
 *
 */
public class Worker extends Thread {
 
	//工作者名
    private String name;
	//第一阶段工作时间
	private long time;
	
	private CountDownLatch countDownLatch;
	
	public Worker(String name, long time, CountDownLatch countDownLatch) {
		this.name = name;
		this.time = time;
		this.countDownLatch = countDownLatch;
	}
	
	@Override
	public void run() {
		try {
			System.out.println(name+"开始工作");
			Thread.sleep(time);
			System.out.println(name+"第一阶段工作完成");
			
			countDownLatch.countDown();
			
			Thread.sleep(2000); //这里就姑且假设第二阶段工作都是要2秒完成
			System.out.println(name+"第二阶段工作完成");
			System.out.println(name+"工作完成,耗费时间="+(time+2000));
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}	
	}
}

Test 类如下:

package com.concurrent.test5;
 
import java.util.concurrent.CountDownLatch;
 
 
public class Test {
 
	public static void main(String[] args) throws InterruptedException {
		CountDownLatch countDownLatch = new CountDownLatch(2);
		Worker worker0 = new Worker("worker0", (long) (Math.random()*2000+3000), countDownLatch);
		Worker worker1 = new Worker("worker1", (long) (Math.random()*2000+3000), countDownLatch);
		Worker worker2 = new Worker("worker2", (long) (Math.random()*2000+3000), countDownLatch);
		
		worker0.start();
		worker1.start();	
		countDownLatch.await();
		
		System.out.println("准备工作就绪");
		worker2.start();
		
	}
 
}

观察运行结果:

worker0开始工作
worker1开始工作
worker1第一阶段工作完成
worker0第一阶段工作完成
准备工作就绪
worker2开始工作
worker1第二阶段工作完成
worker1工作完成,耗费时间=5521
worker0第二阶段工作完成
worker0工作完成,耗费时间=6147
worker2第一阶段工作完成
worker2第二阶段工作完成
worker2工作完成,耗费时间=5384

如此,变可以看出 Join 的短板。

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

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

相关文章

强化学习基础

强化学习的三种方法 基于价值(value-based) 基于策略(policy-based) 基于模型(model-based) 一 基于价值的方法 基于价值 (Value-Based)这种方法,目标是优化价值函数V(s)。 价值函数会告诉我们…

LeetCode 1669. 合并两个链表(C++)

思路: 该题思路很简单,对于单向链表,先遍历到指定的右边界的位置b1,做好标记供连接; 然后对于a-1位置的结点,连接list2,并最后连接后半段的list1 1.题目如下: 给你两个链表 list1 …

ATX agent+UIautomation2 自动化测试介绍

目前ATXUIautomator2 处于自动化界的浪口风尖,现在有幸终于有时间对ATX进行了粗浅的了解 为什么要用ATX ATXUIautomator2的优势: 1.速度吊打appnium,群里面的人这样说的 运行速度快,比Appium运行速度快了好多。(用…

分布式架构

目录 一、前言 二、分布式架构的发展历史 三、分布式架构发展的里程碑 四、分布式系统的意义 五、分布式架构的常见概念 六、分布式领域中冯诺依曼模型的变化 七、分布式系统的难点 八、总结 一、前言 ​  我们都知道,当今无论在BAT这样的大公司&#xff…

Install Linux on Windows with WSL2 (使用 WSL2 在 Windows 上安装 Linux)

Install Linux on Windows with WSL2 (使用 WSL2 在 Windows 上安装 Linux)https://learn.microsoft.com/en-us/windows/wsl/ 在 Windows 上运行 Linux - 在 Windows 11 上运行 Ubuntu 20.04 LTS Developers can access the power of both Windows and Linux at the same tim…

实验十三、阻容耦合共射放大电路的频率响应

一、题目 利用 Multism 从以下几个方面研究图1所示的阻容耦合共射放大电路的频率响应。图1阻容耦合共射放大电路图1\,\,阻容耦合共射放大电路图1阻容耦合共射放大电路(1)设 C1C210μFC_1C_210\,\textrm{μF}C1​C2​10μF,分别测试它们所确定…

6万字电力行业系统解决方案光伏电站综合安防系统解决方案

【版权声明】本资料来源网络,知识分享,仅供个人学习,请勿商用。【侵删致歉】如有侵权请联系小编,将在收到信息后第一时间删除!完整资料领取见文末,部分资料内容: 目录 第 一 章背景与需求 1.1行…

2023年收银管理系统排行榜新鲜出炉

随着新零售的模式普及,越来越多的零售店选择了用收银系统代替收银机。因为收银系统不仅具备收银等功能,其实还有各种店铺数据管理功能、经销商信息管理,销售数据分析等许多功能等。所以如果想清晰地知道门店每天盈利情况和库存情况和采购过程…

这些小众却足够惊艳的素材库,你知道吗?

推荐几个我经常使用的网站,绝对够惊艳。 1、菜鸟图库(免费设计素材) https://www.sucai999.com/?vNTYxMjky 这是一个为新手设计师提供免费素材的设计网站,站内有超多平面模板、海报、UI设计、电商设计等相关素材,质…

JS设计模式

文章目录1 什么是设计模式?2 发布-订阅模式2.1 DOM事件2.2 基于Broadcast Channel实现跨页面通信2.3 基于localStorage实现跨页面通信2.4 使用 Vue 的 EventBus 进行跨组件通信2.4 使用 React 的 EventEmitter 进行跨组件通信3 装饰器模式3.1 React 高阶组件 HOC3.2…

MySql图形化界面工具--DataGrip安装和使用

一、下载安装 1、官网下载安装包,双击开始安装 2、点击next,一步一步的完成安装 3、选择DataGrip的安装目录,然后选择下一步 4、下一步,执行安装 二、使用 1、添加数据源 配置以及驱动jar包下载完毕之后,…

Pod基本概念与Pod应用生命周期

Pod是一个逻辑抽象概念,kubernetes创建和管理的最小单元,一个Pod由一个容器或多个容器组成。特点:一个Pod可以理解为是一个应用实例,提供服务Pod中容器始终部署在一个Node上Pod中容器共享网络、存储资源Pod主要用法:运…

大件传输的9种方法

不知道你有没有试过用电子邮件进行大文件传输,由于文件大小的限制,往往会发送失败。同时,一些文件共享服务对传输的文件有大小限制,使得你无法与朋友分享电影片段或向客户展示你的工作样本。还有一些要求你注册一个账户&#xff0…

【JVM基础内容速查表】JVM基础知识 默认参数 GC命令 工具使用 JVM参数设置、说明、使用方法、注意事项等(持续更新)

目录一、JVM前置知识1. -X、-XX含义2. JVM参数值的类型和设置方式3. 查看GC时用到的命令和JVM参数4. 查看JVM默认参数二、垃圾收集器选择-XX:UseSerialGC-XX:UseParallelGC-XX:UseParallelOldGC-XX:UseParNewGC-XX:UseConcMarkSweepGC-XX:UseG1GC三、垃圾收集器特有参数1. ParN…

pyinstaller打包遇到的问题

1、ModuleNotFoundError: No module named ‘scipy.spatial.transform_rotaion_groups’ 解决办法:–hidden-import scipy.spatial.transform._rotation_groups 2、FileNotFoundError:[Errno 2] No such file or directory:‘C:\Users\Gw0021\AppData\Local\Temp\_M…

leaflet 选择一个marker,点击后设置其为中心点(070)

第070个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中引用geojson文件,显示marker,点击某个marker后,设置其为中心点,并panTo到中心点。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共81行)相…

【数据库】MySQL 索引视图详解

目录 MySQL索引视图 视图 一,什么是视图 二,为什么需要视图 三,视图的作用和优点 四,创建视图 案例: 五,视图使用规则 六, 修改视图 1,修改列名 2,创建复杂视图…

把盏言欢,款款而谈,当WorkPlus接入了ChatGPT机器人

ChatGPT到底有多火? “谷歌AI聊天机器人出错,市值一夜蒸发7172亿”;“百度类ChatGPT项目‘文心一言’或将直接接入百度搜索”;“阿里确认正研发类ChatGPT产品,目前处于内测阶段”;“网易有道将推出教育场景…

电脑技巧:分享六个小众且非常实用的工具

❤️作者主页:IT技术分享社区 ❤️作者简介:大家好,我是IT技术分享社区的博主,从事C#、Java开发九年,对数据库、C#、Java、前端、运维、电脑技巧等经验丰富。 ❤️荣誉: CSDN博客专家、数据库优质创作者🏆&…

黑马】后台管理-路由懒加载

当打包构建项目时, JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。具体需要 3 步:① 安装 babel/plugin-synt…