CountDownLatch介绍

CountDownLatch概述

  1. CountDownLatch一般用作多线程倒计时计数器,强制它们等待其他一组(CountDownLatch的初始化决定)任务执行完成。
  2. 有一点要说明的是CountDownLatch初始化后计数器值递减到0的时候,不能再复原的,这一点区别于SemaphoreSemaphore是可以通过release操作恢复信号量的。

CountDownLatch使用原理

使用原理

  1. 创建CountDownLatch并设置计数器值。
  2. 启动多线程并且调用CountDownLatch实例的countDown()方法。
  3. 主线程调用 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

从运行结果可以看出:

  1. 主线程先打印do something else 1do something else 2。因为startSignal.countDown();完后,count才为0,子线程才能打印。
  2. 因为startSignal.await();是在子线程内,所有子线程都等待startSignal.countDown()执行后才能打印do work!
  3. 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使用详解的更多相关文章

  1. 最强Java并发编程详解:知识点梳理,BAT面试题等

    本文原创更多内容可以参考: Java 全栈知识体系.如需转载请说明原处. 知识体系系统性梳理 Java 并发之基础 A. Java进阶 - Java 并发之基础:首先全局的了解并发的知识体系,同时了解 ...

  2. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

  3. Java 序列化Serializable详解

    Java 序列化Serializable详解(附详细例子) Java 序列化Serializable详解(附详细例子) 1.什么是序列化和反序列化Serialization(序列化)是一种将对象以一连 ...

  4. Java String类详解

    Java String类详解 Java字符串类(java.lang.String)是Java中使用最多的类,也是最为特殊的一个类,很多时候,我们对它既熟悉又陌生. 类结构: public final ...

  5. 最新java数组的详解

    java中HashMap详解 http://alex09.iteye.com/blog/539545 总结: 1.就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并不是真正的把 Java ...

  6. JAVA IO 类库详解

    JAVA IO类库详解 一.InputStream类 1.表示字节输入流的所有类的超类,是一个抽象类. 2.类的方法 方法 参数 功能详述 InputStream 构造方法 available 如果用 ...

  7. 转:Java HashMap实现详解

    Java HashMap实现详解 转:http://beyond99.blog.51cto.com/1469451/429789 1.    HashMap概述:    HashMap是基于哈希表的M ...

  8. 淘宝JAVA中间件Diamond详解(2)-原理介绍

    淘宝JAVA中间件Diamond详解(二)---原理介绍 大家好,通过第一篇的快速使用,大家已经对diamond有了一个基本的了解.本次为大家带来的是diamond核心原理的介绍,主要包括server ...

  9. 【转】 java中HashMap详解

    原文网址:http://blog.csdn.net/caihaijiang/article/details/6280251 java中HashMap详解 HashMap 和 HashSet 是 Jav ...

随机推荐

  1. 负载均衡服务之HAProxy基础配置(五)

    前文我们聊了下haproxy的修改报文首部的配置.压缩功能以及haproxy基于http协议自定义健康状态检测机制:回顾请参考https://www.cnblogs.com/qiuhom-1874/p ...

  2. Auth认证中的think_auth_rule type字段干嘛用的?

    昨晚认真研究了一下这个类,设计的很巧妙,但是你说的这个字段,我认为应该是作者想加功能但还没写,在session判断的地方可以看到,type这个字段实际是对应的 1-实时验证,2登陆验证 ,显然,这个字 ...

  3. c++ 更新 performance counter 数据,错误码 87

    ERROR_INVALID_PARAMETER 87 (0x57) The parameter is incorrect. 很可能是该送 ULONG 的送了 ULONGLONG,vise versa

  4. 企业级 Harbor 镜像仓库

    Harbor是由VMWare公司开源的容器镜像仓库.事实上,Harbor是在Docker Registry上进行了相应 的企业级扩展,从而获得了更加广泛的应用,这些新的企业级特性包括:管理用户界面,基 ...

  5. gitlab环境部署

    一:配置主机名 [root@localhost ~]# hostname gitlab[root@localhost ~]# bash 二:安装依赖包 [root@gitlab ~]# yum -y ...

  6. python- 函数高级

    函数高级 一.默认参数 1.默认参数概念 默认参数指函数/方法在定义时为形参赋值,对应的形参称为默认参数. 默认参数是一个参数定义期的概念,与调用无关. 2.默认参数的作用 如果参数定义默认参数,在调 ...

  7. 瑞银预计小扎的十年规划可获大回报 上调Facebook股票目标价

    瑞银认为马克·扎克伯格(Mark Zuckerberg)为Facebook定制的十年规划将带来丰厚回报,它已将Facebook股票的目标价由之前的155美元上调至165美元. Facebook首席执行 ...

  8. Codeforce 1255 Round #601 (Div. 2) A. Changing Volume (贪心)

    Bob watches TV every day. He always sets the volume of his TV to bb. However, today he is angry to f ...

  9. 一只简单的网络爬虫(基于linux C/C++)————配置文件设计及读取

    一般来说linux下比较大型的程序都是以配置文件作为参数介质传递的,该爬虫也采用配置文件的方式来获取参数,配置文件格式大致如下: max_job_num=1 #seeds=https://www.ba ...

  10. Servlet 教程——检视阅读

    Servlet 教程--检视阅读 参考 Servlet教程--菜鸟--蓝本 Servlet教程--w3cschool Servlet教程--易百 servlet依赖maven依赖: <!--se ...