等待多线程完成的CountDownLatch(带示例)
开始磨刀霍霍向多线程了,这期是 CountDownLatch 的一个小示例。
定义:CountDownLatch 允许一个或多个线程等待其他线程完成操作。
应用需求举例:假设有4个线程,A、B、C、D,线程 D 需要在 A、B、C 执行完之后再执行。
应用需求分析:如上描述,如果想让线程 D 最后执行,结合之前的学习,我们可以采用 join() 方法来实现,比如在 A 线程调 B.join(),B 线程调用 C.join() 等等,我们来回忆一下 join() 方法:一个线程调用另一个线程的 join() 方法时,当前线程阻塞,等待被调用 join() 方法的线程执行完毕才能继续执行,这样可以保证线程执行顺序。
如下为 join() 方法实现:
public class Demo {
public static void main(String[] args) {
Thread A = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("A");
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("B 开始等待 A");
try {
A.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B");
}
});
Thread C = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("C 开始等待 B");
try {
B.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("C");
}
});
Thread D = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("D 开始等待 C");
try {
C.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("D");
}
});
B.start();
A.start();
C.start();
D.start();
}
}
执行结果如下:
B 开始等待 A
C 开始等待 B
A
D 开始等待 C
B
C
D
显然 join() 方法是可以完成 D 线程最后执行的要求,但是却失去了多线程并行执行的意义,怎么讲?
因为线程 A、B、C 无法完成同步运行,本质上还是串行,join() 方法其内部的实现是基于等待通知机制。
为了解决这个问题,我们可以利用 CountdownLatch 来实现这类通信方式,用法如下:
- CountdownLatch 构造函数接收一个 int 类型的参数作为计数器,如果想等待 N 个点,就传入 N
- 调用 CountdownLatch 的 countDown() 方法时,计数器 N 就会减 1
- 在等待线程中调用 await() 方法,会进入等待状态,直到计数器 N 变为 0 后再执行;
CountdownLatch 方式实现:
public class C {
public static void main(String[] args) {
int N = 3;
CountDownLatch countDownLatch = new CountDownLatch(N);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("D 等待执行中...");
try {
countDownLatch.await();
System.out.println("D 开始执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
for (char threadName='A'; threadName <= 'C'; threadName++) {
final String tN = String.valueOf(threadName);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(tN + " 正在执行");
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(tN + " 执行完毕");
countDownLatch.countDown();
}
}).start();
}
}
}
执行结果如下:
D 等待执行中...
B 正在执行
A 正在执行
C 正在执行
C 执行完毕
A 执行完毕
B 执行完毕
D 开始执行
应用场景模拟:导出用户账单明细,按年月日三个维度进行计算并显示,年月日分别放入 3 个 Excel 中,最终将Excel 合并为一个 zip 压缩文件返回给用户。
public class Demo2 {
public static void main(String[] args) {
try {
// 模拟跟路径,根据自己路径修改
String tempDir = "/C/workspace/File/excel";
// 模拟存放文件路径
String baseFileName = "202009171016";
String zipFileName = tempDir + "/" + baseFileName + ".zip";
int N = 3;
CountDownLatch mDoneSignal = new CountDownLatch(N);
ExecutorService executor = Executors.newFixedThreadPool(N);
// 用户盛放 excel 路径
List<String> excelFileNames = new ArrayList<String>();
for (int i = 0; i < 3; i++) {
File file = new File(tempDir + "/" + baseFileName + "_" + i + ".xls");
file.createNewFile();
excelFileNames.add(tempDir + "/" + baseFileName + "_" + i + ".xls");
executor.execute(new MyThread(file, i, mDoneSignal));
}
// 关闭启动线程
executor.shutdown();
// 等待子线程结束,再继续执行下面的代码
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
String[] fileNames = excelFileNames.toArray(new String[excelFileNames.size()]);
try {
mDoneSignal.await();// 等待所有工作线程结束
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有数据处理完毕...");
System.out.println("开始将数据打包zip...");
}catch (Exception e) {
e.printStackTrace();
}
}
/**
* 模拟线程
*/
public static class MyThread implements Runnable{
private final File file;
private final CountDownLatch mDoneSignal;
private final int index;
public MyThread(File file, int index, CountDownLatch mDoneSignal) {
this.index = index;
this.file = file;
this.mDoneSignal = mDoneSignal;
}
@Override
public void run() {
// 线程执行逻辑,处理 工作簿 等
switch (index){
case 0:
System.out.println("线程A执行完毕:" + LocalTime.now());
break;
case 1:
System.out.println("线程B执行完毕:" + LocalTime.now());
break;
case 2:
System.out.println("线程C执行完毕:" + LocalTime.now());
break;
}
// 线程完成,计数器减一
mDoneSignal.countDown();
}
}
}
执行结果如下:
线程C执行完毕:10:38:01.454
线程B执行完毕:10:38:01.455
线程A执行完毕:10:38:01.455
所有数据处理完毕...
开始将数据打包zip...
博客持续更新,关注订阅,未来,我们一起成长。
本文首发于博客园:https://www.cnblogs.com/niceyoo/p/13690231.html
等待多线程完成的CountDownLatch(带示例)的更多相关文章
- Java并发工具类(一):等待多线程完成的CountDownLatch
作用 CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行 简介 CountDownLatch是在java1.5被引入的,存在于java.uti ...
- 并发工具类(一)等待多线程的CountDownLatch
前言 JDK中为了处理线程之间的同步问题,除了提供锁机制之外,还提供了几个非常有用的并发工具类:CountDownLatch.CyclicBarrier.Semphore.Exchanger.Ph ...
- Java多线程系列——计数器 CountDownLatch
简介: CountDownLatch 是一个非常实用的多线程控制工具类,通常用来控制线程的等待,它可以让某个线程等待直到倒计时结束 CountDownLatch 提供了两个主要的方法,await(). ...
- CyclicBarrier 是如何做到等待多线程到达一起执行的?
我们有些场景,是需要使用 多线各一起执行某些操作的,比如进行并发测试,比如进行多线程数据汇总. 自然,我们可以使用 CountDownLatch, CyclicBarrier, 以及多个 Thread ...
- java多线程系列(八)---CountDownLatch和CyclicBarrie
CountDownLatch 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线 ...
- Java多线程之---用 CountDownLatch 说明 AQS 的实现原理
本文基于 jdk 1.8 . CountDownLatch 的使用 前面的文章中说到了 volatile 以及用 volatile 来实现自旋锁,例如 java.util.concurrent.ato ...
- java多线程---------java.util.concurrent并发包----------等待多线程完成
一.等待多线程完成的join的使用.CoundownLantch.CyclicBarrier .
- 【转】oozie安装和自带示例的使用
oozie安装 [转]http://www.tuicool.com/articles/qUVNJn oozie自带示例的使用 [转]http://blog.csdn.net/zhu_xun/artic ...
- java高并发系列 - 第16天:JUC中等待多线程完成的工具类CountDownLatch,必备技能
这是java高并发系列第16篇文章. 本篇内容 介绍CountDownLatch及使用场景 提供几个示例介绍CountDownLatch的使用 手写一个并行处理任务的工具类 假如有这样一个需求,当我们 ...
随机推荐
- Oracle初始——第一天
一.数据库分类 1.小型数据库:access.foxbase 2.中型数据库:informix.sql server.mysql 3.大型数据库:sybase.db2.oracle 二.注释 1./* ...
- iptables从入门到掌握
防火墙简述 防火墙(firewall)一词本是建筑用于,本意是为了保护建筑物不受火灾侵害的.被借鉴到了在网络通信领域中,表示保护局域网或主机不受网络攻击的侵害. 防火墙: 工作在主机或者网络边缘,对于 ...
- https中引入http资源资源所导致的问题
问题描述 因为公司要求所有生产环境为了安全性需求,全部都走https, 并且在Nginx里面加入了Content-Security-Policy "upgrade-insecure-requ ...
- 为什么重写 equals() 方法,一定要重写 hashCode() 呢?| HashMap
微信搜索「码农田小齐」,关注这个在纽约的程序媛,回复「01-05」可以获取计算机精选书籍.个人刷题笔记.大厂面经.面试资料等资源,么么哒- 首先我们有一个假设:任何两个 object 的 hashCo ...
- JS仿贪吃蛇:一串跟着鼠标的Div
贪吃蛇是一款80后.90后比较熟悉的经典游戏,下面通过简单的JS代码来实现低仿版贪吃蛇效果:随着鼠标的移动,在页面中呈现所有Div块跟随鼠标依次移动,效果如下图所示. <!DOCTYPE htm ...
- 大数据分析中数据治理的重要性,从一个BI项目的失败来分析
很多企业在做BI项目时,一开始的目标都是想通过梳理管理逻辑,帮助企业搭建可视化管理模型与深化管理的精细度,及时发现企业经营管理中的问题. 但在项目实施和验收时,BI却变成了报表开发项目,而报表的需求往 ...
- Internet 网络协议族
1.linux目前支持多种协议族,每个协议族用一个net_porto_family结构实例来表示,在初始化时,会调用sock_register()函数初始化注册到net_families[NPROTO ...
- UNP——第六章,多路转接IO——select
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ...
- 微服务通信之feign的配置隔离
前言 由上文我们知道针对某一个Feign接口,我们可以给他设置特定的配置类.那如果现在有一个服务,我们只想对A服务配置一个拦截器拦截请求而不影响其他服务,那应该怎么做呢? 一.feign接口配置 由前 ...
- 05 . Vue前端交互,fetch,axios,以asyncawait方式调用接口使用及案例
目标 /* 1. 说出什么是前后端交互模式 2. 说出Promise的相关概念和用法 3. 使用fetch进行接口调用 4. 使用axios进行接口调用 5. 使用asynnc/await方式调用接口 ...