CountDownLatch官方文档

一、原理

  CountDownLatch是一个非常实用的多线程控制工具类。Count Down在英文中意为倒计时,Latch意为门闩,可以简单的将CountDownLatch称为倒计时器。门闩的含义是:把门锁起来,不让里面的线程跑出来。因此,这个工具通常用来控制线程等待,它可以让一个线程等待知道倒计时结束,再开始执行。

  CountDownLatch内部维护着一个count计数,只不过对这个计数器的操作都是原子操作,同时只能有一个线程去操作这个计数器。CountDownLatch通过构造函数传入一个初始计数值,调用者可以通过调用CounDownLatch对象的countDown()方法,来使计数减1;如果调用对象上的await()方法,那么调用者就会一直阻塞在这里,直到别人通过countDown方法,将计数减到0,才可以继续执行。

  CountDownLatch的一种典型应用场景是火箭发射。在火箭发射前,为了保证万无一失,往往要进行各项设备、仪器的检查。只有等所有的检查完毕后,引擎才能点火。这种场景就非常适合使用CountDownLatch。它可以使得点火线程等待所有检查线程全部完成后再执行。

二、API

主要方法

  • public CountDownLatch(int count);

    构造方法参数指定了计数的次数

  • public void countDown();

    当前线程调用此方法,则计数减一

  • public void await() throws InterruptedException

    调用此方法会一直阻塞当前线程,直到计时器的值为0

三、用法

CountDownLatch的一种典型用法是:

a group of worker threads use two countdown latches(一组工作线程使用两个CountDownLatch)。

Sample usage: a group of worker threads use two countdown latches(一组工作线程使用两个CountDownLatch):

  • The first is a start signal that prevents any worker from proceeding until the driver is ready for them to proceed;
  • The second is a completion signal that allows the driver to wait until all workers have completed

下面以一个Demo来说明CountDownLatch的使用。

程序功能:模拟多线程下载,合并线程等到所有下载任务完成便开始合并。

/**
* CountDownLatch的典型用法2
* 代码功能:使用多个下载线程实现下载,等到所有任务完成后合并线程开始合并。
*
* @author lp
*
*/
public class CountDownLatchDemo2 { public static void main(String[] args) throws InterruptedException {
int N = 8;
CountDownLatch startSignal = new CountDownLatch(1);//startSignal控制开始
CountDownLatch doneSignal = new CountDownLatch(N);// ExecutorService executor = Executors.newFixedThreadPool(N + 1);// 8个下载线程,1个合并线程 // let all threads proceed
startSignal.countDown(); // N个下载任务开始执行
for (int i = 0; i < N; ++i) {
executor.execute(new DownloadRunnable(startSignal,doneSignal, i));
} // wait for all to finish
doneSignal.await();//阻塞,直到计数器为0 // 执行合并任务
executor.execute(new MergeRunnable()); executor.shutdown();
}
} /**
* 下载线程
*/
class DownloadRunnable implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
private final int i; public DownloadRunnable(CountDownLatch startSignal, CountDownLatch doneSignal, int i) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
this.i = i;
} public void run() {
try {
startSignal.await();//当前线程等待
doDownload(i);
doneSignal.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
} } private void doDownload(int i) {
System.out.println("Thead:" + i + "下载完成");
}
}

Another typical usage would be to divide a problem into N parts, describe each part with a Runnable that executes that portion and counts down on the latch, and queue all the Runnables to an Executor. When all sub-parts are complete, the coordinating thread will be able to pass through await. (When threads must repeatedly count down in this way, instead use a CyclicBarrier.)

另外一种典型的用法是:将一个问题分解成N部分,用Runnable来描述每一部分任务,每个Runnable执行完后将latch数减1,而且要将所有的Runnable都加入到Executor中。当所有子部分任务完成时,协调线程经过await后能够开始执行。(当线程必须可重复性的倒计时时,请使用CyclicBarrier代替)

/**
* CountDownLatch的典型用法1
* 代码功能:模拟多线程下载功能-使用多个下载线程实现下载,等到所有任务完成后合并线程开始合并。
*
* @author lp
*
*/
public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException {
int N = 8;
CountDownLatch doneSignal = new CountDownLatch(N);// 计数器从N开始倒数
ExecutorService executor = Executors.newFixedThreadPool(N + 1);// N个下载线程,1个合并线程 // N个下载任务开始执行
for (int i = 0; i < N; ++i) {
executor.execute(new DownloadRunnable(doneSignal, i));
} // wait for all to finish
doneSignal.await();//阻塞,直到计数器为0 // 执行合并任务
executor.execute(new MergeRunnable()); executor.shutdown();
}
} /**
* 下载线程
*/
class DownloadRunnable implements Runnable {
private final CountDownLatch doneSignal;
private final int i; DownloadRunnable(CountDownLatch doneSignal, int i) {
this.doneSignal = doneSignal;
this.i = i;
} public void run() {
doDownload(i);
doneSignal.countDown();
} private void doDownload(int i) {
System.out.println("Thead:" + i + "下载完成");
}
} /**
* 合并线程
*/
class MergeRunnable implements Runnable{ @Override
public void run() {
doMerge();
} private void doMerge(){
System.out.println("合并线程完成合并");
} }

最新文章

  1. SVN与git交互
  2. Centos安装Kafka集群
  3. #pragma pack(n)
  4. openstack奠基篇:devstack (liberty)于centos 7安装
  5. 259. 3Sum Smaller
  6. Android进阶笔记03:Android应用中实现查看&quot;附近的人&quot;的功能
  7. 【MySQL】关于MySQL错误日志信息的收集
  8. MySQL 5.6 root密码丢失,使用mysqld --skip-grant-tables
  9. testng 提供参数
  10. 怎么去掉word标题前的黑点
  11. 【Bootstrap】兼容IE8、谷歌和其他主流浏览器的观众IMAX风格的页面
  12. HDOJ3743&lt;分治&gt;
  13. 自己写的日志框架--linkinLog4j--日志框架的必要性
  14. Android 之文件夹排序
  15. Linux下GitLab服务器搭建
  16. kafka相关问题集锦
  17. VBA果然很强大
  18. Linux Shell函数返回值
  19. layui动态数据表格-分页
  20. [原]Jenkins(十五)---jenkins插件之deploy

热门文章

  1. MySQL5.7的并行复制
  2. ora600
  3. 像计算机科学家一样思考python-第4章 案例研究:接口设计
  4. 002-es5.4.3结合spring-data-elasticsearch3.0.0.0使用
  5. oracle系统调优
  6. 剑指offer(2):字符串
  7. C# 模拟登陆
  8. 事件驱动体系结构(EDA)
  9. Struts2入门1
  10. SQL Puzzle