Java—CountDownLatch使用详解
CountDownLatch介绍
CountDownLatch概述
CountDownLatch一般用作多线程倒计时计数器,强制它们等待其他一组(CountDownLatch的初始化决定)任务执行完成。- 有一点要说明的是
CountDownLatch初始化后计数器值递减到0的时候,不能再复原的,这一点区别于Semaphore,Semaphore是可以通过release操作恢复信号量的。
CountDownLatch使用原理
使用原理
- 创建CountDownLatch并设置计数器值。
- 启动多线程并且调用CountDownLatch实例的countDown()方法。
- 主线程调用
await()方法,这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务,count值为0,停止阻塞,主线程继续执行。
使用模板
public class CountDownLatchModule {
//线程数
private static int N = 10;
// 单位:min
private static int countDownLatchTimeout = 5;
public static void main(String[] args) {
//创建CountDownLatch并设置计数值,该count值可以根据线程数的需要设置
CountDownLatch countDownLatch = new CountDownLatch(N);
//创建线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < N; i++) {
cachedThreadPool.execute(() ->{
try {
System.out.println(Thread.currentThread().getName() + " do something!");
} catch (Exception e) {
System.out.println("Exception: do something exception");
} finally {
//该线程执行完毕-1
countDownLatch.countDown();
}
});
}
System.out.println("main thread do something-1");
try {
countDownLatch.await(countDownLatchTimeout, TimeUnit.MINUTES);
} catch (InterruptedException e) {
System.out.println("Exception: await interrupted exception");
} finally {
System.out.println("countDownLatch: " + countDownLatch.toString());
}
System.out.println("main thread do something-2");
//若需要停止线程池可关闭;
// cachedThreadPool.shutdown();
}
运行结果:
main thread do something-1
pool-1-thread-1 do something!
pool-1-thread-2 do something!
pool-1-thread-3 do something!
pool-1-thread-5 do something!
pool-1-thread-6 do something!
pool-1-thread-7 do something!
pool-1-thread-8 do something!
pool-1-thread-4 do something!
pool-1-thread-9 do something!
pool-1-thread-10 do something!
countDownLatch: java.util.concurrent.CountDownLatch@76fb509a[Count = 0]
main thread do something-2
CountDownLatch常用方法

public void await() throws InterruptedException:调用await()方法的线程会被挂起,等待直到count值为0再继续执行。public boolean await(long timeout, TimeUnit unit) throws InterruptedException:同await(),若等待timeout时长后,count值还是没有变为0,不再等待,继续执行。时间单位如下常用的毫秒、天、小时、微秒、分钟、纳秒、秒。

public void countDown(): count值递减1.public long getCount():获取当前count值。public String toString():重写了toString()方法,多打印了count值,具体参考源码。
CountDownLatch使用场景
一个程序中有N个任务在执行,我们可以创建值为N的CountDownLatch,当每个任务完成后,调用一下countDown()方法进行递减count值,再在主线程中使用await()方法等待任务执行完成,主线程继续执行。
CountDownLatch源码
构造方法源码
/**
* Constructs a {@code CountDownLatch} initialized with the given count.
*
* @param count the number of times {@link #countDown} must be invoked
* before threads can pass through {@link #await}
* @throws IllegalArgumentException if {@code count} is negative
*/
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
toString()方法源码
/**
* Returns a string identifying this latch, as well as its state.
* The state, in brackets, includes the String {@code "Count ="}
* followed by the current count.
*
* @return a string identifying this latch, as well as its state
*/
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
CountDownLatch示例
作为线程启动信号
代码
public class CountDownLatchTest {
/**
* a start signal that prevents any worker from proceeding
* until the driver is ready for them to proceed;
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
// create and start threads
new Thread(new Worker(startSignal, doneSignal)).start();
}
// don't let run yet
System.out.println("do something else 1");
// let all threads proceed
startSignal.countDown();
System.out.println("do something else 2");
// wait for all to finish
doneSignal.await();
System.out.println("wait for all to finsh");
}
static class Worker implements Runnable{
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
@Override
public void run() {
try {
startSignal.await();
doWork();
doneSignal.countDown();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
void doWork() {
System.out.println("do work!");
}
}
}
运行结果
do something else 1
do something else 2
do work!
do work!
do work!
do work!
do work!
do work!
do work!
do work!
do work!
do work!
wait for all to finsh
从运行结果可以看出:
- 主线程先打印
do something else 1和do something else 2。因为startSignal.countDown();完后,count才为0,子线程才能打印。 - 因为
startSignal.await();是在子线程内,所有子线程都等待startSignal.countDown()执行后才能打印do work!。 doneSignal.await();等待所有子线程执行后,每次都doneSignal.countDown(),最后count为0,主线程才执行打印wait for all to finsh。
作为线程等待完成信号
代码
public class CountDownLatchTest2 {
/**
* a completion signal that allows the driver to wait
* until all workers have completed.
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
CountDownLatch doneSignal = new CountDownLatch(5);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
// create and start threads
cachedThreadPool.execute(new Worker(doneSignal, i));
}
// don't let run yet
System.out.println("do something else 1");
// wait for all to finish
doneSignal.await();
System.out.println("===========================count: " + doneSignal.getCount());
System.out.println("do something else 2");
cachedThreadPool.shutdown();
}
static class Worker implements Runnable{
private final CountDownLatch doneSignal;
private final int i;
Worker(CountDownLatch doneSignal, int i) {
this.doneSignal = doneSignal;
this.i = i;
}
@Override
public void run() {
try {
doWork();
doneSignal.countDown();
System.out.println("i = " + i + ", " + doneSignal.toString());
} catch (Exception ex) {
ex.printStackTrace();
}
}
void doWork() {
System.out.println("do work!");
}
}
}
运行结果
do something else 1
do work!
i = 0, java.util.concurrent.CountDownLatch@128abd43[Count = 4]
do work!
i = 1, java.util.concurrent.CountDownLatch@128abd43[Count = 3]
do work!
i = 2, java.util.concurrent.CountDownLatch@128abd43[Count = 2]
do work!
i = 3, java.util.concurrent.CountDownLatch@128abd43[Count = 1]
do work!
i = 4, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
===========================count: 0
do something else 2
do work!
i = 5, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 6, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 7, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 8, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 9, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
从运行结果可以看出,主线程是等待其他线程运行了5次结束后就打印了do something else 2信息,因为CountDownLatch数值为5。
Java—CountDownLatch使用详解的更多相关文章
- 最强Java并发编程详解:知识点梳理,BAT面试题等
本文原创更多内容可以参考: Java 全栈知识体系.如需转载请说明原处. 知识体系系统性梳理 Java 并发之基础 A. Java进阶 - Java 并发之基础:首先全局的了解并发的知识体系,同时了解 ...
- Java 字符串格式化详解
Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...
- Java 序列化Serializable详解
Java 序列化Serializable详解(附详细例子) Java 序列化Serializable详解(附详细例子) 1.什么是序列化和反序列化Serialization(序列化)是一种将对象以一连 ...
- Java String类详解
Java String类详解 Java字符串类(java.lang.String)是Java中使用最多的类,也是最为特殊的一个类,很多时候,我们对它既熟悉又陌生. 类结构: public final ...
- 最新java数组的详解
java中HashMap详解 http://alex09.iteye.com/blog/539545 总结: 1.就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并不是真正的把 Java ...
- JAVA IO 类库详解
JAVA IO类库详解 一.InputStream类 1.表示字节输入流的所有类的超类,是一个抽象类. 2.类的方法 方法 参数 功能详述 InputStream 构造方法 available 如果用 ...
- 转:Java HashMap实现详解
Java HashMap实现详解 转:http://beyond99.blog.51cto.com/1469451/429789 1. HashMap概述: HashMap是基于哈希表的M ...
- 淘宝JAVA中间件Diamond详解(2)-原理介绍
淘宝JAVA中间件Diamond详解(二)---原理介绍 大家好,通过第一篇的快速使用,大家已经对diamond有了一个基本的了解.本次为大家带来的是diamond核心原理的介绍,主要包括server ...
- 【转】 java中HashMap详解
原文网址:http://blog.csdn.net/caihaijiang/article/details/6280251 java中HashMap详解 HashMap 和 HashSet 是 Jav ...
随机推荐
- C# 基础知识系列- 14 IO篇之入门IO
0. 前言 在之前的章节中,大致介绍了C#中的一些基本概念.这篇我们将介绍一下C#的I/O操作,这将也是一个小连续剧.这是第一集,我们先来简单了解一下C#中的I/O框架. 1. 什么是I/O I/O ...
- thinkphp if便签的使用
<foreach name="list" item='v'> <tr> <td><img class="user" s ...
- unset变量释放内存不起作用
unset()函数只能在变量值占用内存空间超过256字节时才会释放内存空间. 只有当指向该变量的所有变量(如引用变量)都被销毁后,才会释放内存.
- QT QLabel内容太长时候使用省略号
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/xiezhongyuan07/articl ...
- QtConcurrent::run() 只能运行参数个数不超过5的函数
有时不得不看源码 qtconcurrentrun.h template <typename T, typename Param1, typename Arg1, typename Param2, ...
- 【ElasticSearch学习】之一图读懂文档索引全过程
ES索引过程详解: 1.客户端发送索引请求. 客户端向ES节点发送索引请求,以RestClient客户端发起请求为例: ES提供了Java High Level REST Client,用户可以通过R ...
- 数学--数论---P4718 Pollard-Rho算法 大数分解
P4718 [模板]Pollard-Rho算法 题目描述 MillerRabin算法是一种高效的质数判断方法.虽然是一种不确定的质数判断法,但是在选择多种底数的情况下,正确率是可以接受的.Pollar ...
- 怎么避免写出慢SQL
在大多数实际的系统中,慢 SQL 消耗掉的数据库资源,往往是正常 SQL 的几倍.几十倍甚至几百倍. 怎样才能在开发阶段尽量避免写出慢 SQL 呢? 估算数据量 慢 SQL 对数据库的影响,是一个量变 ...
- NetCore项目实战篇03---HTTP Patch 更新数据
一.什么是HTPP Patch HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法. HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 ...
- zabbix日常问题总结
1.connection to database 'zabbix' failed: [1040] Too many connections 问题:数据库连接池太少解决:增加数据库连接池步骤:(1).进 ...