1.基本概念

CountDownLatch,中文名倒数闩,jdk并发工具包中一个并发控制器,它抽象了一个常见的多线程并发场景,开发人员使用它可以写出同时兼顾线程安全性与高效率的代码。

2.抽象模型

相当于是一种进化版本的等待/通知机制,它可以的实现的是一个或多个工作线程完成任务后通知一个或多个等待线程开始工作,jdk中的await/notify、notifyAll是一个工作线程完成任务通知一个等待线程或所有等待的线程。

3.使用场景

运动员田径跑步比赛

4.CountDownLatch使用api

<1>CountDownLatch latch = new CountDownLatch(n); //初始化倒数闩

<2>latch.await()或latch.await(long timeout,TimeUnit unit); //执行线程等待,直到latch倒数为0(超时后中断或线程被中断)

<3>latch.countDown(); //执行线程把latch倒数数减1,直到为0,会通知所有被这个计数器阻塞的线程;如果当前计数器latch为0,调用countDown(),不会继续减1;

5.使用示例(田径比赛)

    public static void main(String[] args) throws InterruptedException {
CountDownLatch beginLatch = new CountDownLatch(1); // 起跑倒数闩
CountDownLatch endLatch = new CountDownLatch(10); // 结束倒数闩 // 创建10个线程,代表是个运动员,他们的主要任务就是预备准备、和跑步两个阶段
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 1; i < 11; i++) {
final int num = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
// 准备阶段
long a = (long) (Math.random() * 1000);
Thread.sleep(a);
System.out.println("运动员" + num +"号,准备了" + a + "s,准备完毕!");
beginLatch.await();
// 跑步阶段
long b = (long) (Math.random() * 100);
Thread.sleep(b);
System.out.println("运动员" + num + "号,用时" + b + "s,完成比赛");
endLatch.countDown(); } catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
executor.submit(runnable);
}
//出发信号发出
beginLatch.countDown();
endLatch.await();
System.out.println("跑步结束");
}

细心的你会发现上述的代码存在致命的错误就是当主线程发出了开跑信号beginLatch.countDown(),可能有部分运动员还在准备。运行结果如下:

运动员10号,准备了155s,准备完毕!
运动员10号,用时31s,完成比赛
运动员8号,准备了228s,准备完毕!
运动员8号,用时61s,完成比赛
运动员9号,准备了333s,准备完毕!
运动员3号,准备了335s,准备完毕!
运动员9号,用时61s,完成比赛
运动员3号,用时94s,完成比赛
运动员4号,准备了513s,准备完毕!
运动员4号,用时71s,完成比赛
运动员2号,准备了598s,准备完毕!
运动员6号,准备了616s,准备完毕!
运动员6号,用时39s,完成比赛
运动员2号,用时54s,完成比赛
运动员1号,准备了737s,准备完毕!
运动员5号,准备了805s,准备完毕!
运动员1号,用时80s,完成比赛
运动员5号,用时70s,完成比赛
运动员7号,准备了923s,准备完毕!
运动员7号,用时67s,完成比赛
跑步结束 没有都准备好就开始,控制不合理

根本原因在于这里缺少一个倒数闩,来计数准备好的运动员。我们进行改进如下

    public static void main(String[] args) throws InterruptedException {
CountDownLatch readyLatch = new CountDownLatch(10); //准备好倒数闩
CountDownLatch beginLatch = new CountDownLatch(1); // 起跑倒数闩
CountDownLatch endLatch = new CountDownLatch(10); // 结束倒数闩 // 创建10个线程,代表是个运动员,他们的主要任务就是预备准备、和跑步两个阶段
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 1; i < 11; i++) {
final int num = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
// 准备阶段
long a = (long) (Math.random() * 1000);
Thread.sleep(a);
System.out.println("运动员" + num +"号,准备了" + a + "s,准备完毕!");
readyLatch.countDown();
beginLatch.await();
// 跑步阶段
long b = (long) (Math.random() * 100);
Thread.sleep(b);
System.out.println("运动员" + num + "号,用时" + b + "s,完成比赛");
endLatch.countDown(); } catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
executor.submit(runnable);
}
readyLatch.await();
//出发信号发出
beginLatch.countDown();
endLatch.await();
System.out.println("跑步结束");
}

改进后运行结果

运动员8号,准备了112s,准备完毕!
运动员3号,准备了119s,准备完毕!
运动员5号,准备了463s,准备完毕!
运动员6号,准备了579s,准备完毕!
运动员2号,准备了597s,准备完毕!
运动员1号,准备了825s,准备完毕!
运动员7号,准备了849s,准备完毕!
运动员9号,准备了876s,准备完毕!
运动员4号,准备了931s,准备完毕!
运动员10号,准备了930s,准备完毕!
运动员6号,用时0s,完成比赛
运动员5号,用时19s,完成比赛
运动员9号,用时24s,完成比赛
运动员8号,用时56s,完成比赛
运动员3号,用时62s,完成比赛
运动员1号,用时77s,完成比赛
运动员7号,用时84s,完成比赛
运动员4号,用时85s,完成比赛
运动员2号,用时88s,完成比赛
运动员10号,用时86s,完成比赛
跑步结束

最新文章

  1. 基于C/S架构的3D对战网络游戏C++框架 _02系统设计(总体设计、概要设计)
  2. iOS 阶段学习第25天笔记(iOS沙盒机制介绍)
  3. iOS ARC中CTCallCenter没用,无法监听电话的解决方案
  4. AngularJS向指令传递数据
  5. C++多继承
  6. android中viewPager+fragment实现的屏幕左右切换(进阶篇)
  7. 使用TransactionScope做分布式事务协调
  8. 福州大学软工1715|W班-启航
  9. 18 徐州 M
  10. Dockerfile的常见命令
  11. 整合shiro出现【Correct the classpath of your application so that it contains a single, compatible version of org.quartz.Scheduler】
  12. 近年NOIP考点与主要做法
  13. Linux 小知识翻译 - 「Linux」怎么读?
  14. linux一些基本知识
  15. mysql分组GROUP BY常用sql
  16. avalon 如何隐藏首屏加载页面时出现的花括号
  17. BZOJ.2705.[SDOI2012]Longge的问题(莫比乌斯反演 欧拉函数)
  18. Linux内存分配机制之伙伴系统和SLAB
  19. Time range (447392) for take &#39;Take 001&#39; is larger than maximum allowed(100000).
  20. Highcharts做柱状图怎样样每个柱子都是不同的颜色显示

热门文章

  1. ubuntu 14.04 重装机 安装笔记 无线网卡+cuda+nvidia
  2. 部署Qt应用时候报错0xc000007b
  3. 陕西柴油机--机械ip--------》QQ请求汇创
  4. 六、Django模型基础第一节
  5. Visual C++ 6.0中关于for的简单问题
  6. android textiew自定义ClickableSpan无效问题
  7. 开发Canvas 绘画应用(三):实现对照绘画
  8. 使用Docker搭建LNMP开发环境
  9. HDU - 5755:Gambler Bo (开关问题,%3意义下的高斯消元)
  10. java中二维数组内存分配