前言

在多线程环境下,JDK给开发者提供了许多的组件供用户使用(主要在java.util.concurrent下),使得用户不需要再去关心在具体场景下要如何写出同时兼顾线程安全性与高效率的代码。之前讲过的线程池、BlockingQueue都是在java.util.concurrent下的组件,Timer虽然不在java.util.concurrent下,但也算是。后两篇文章将以例子的形式简单讲解一些多线程下其他组件的使用,不需要多深刻的理解,知道每个组件大致什么作用就行。

本文主要讲解的是CountDownLatch

CountDownLatch

CountDownLatch主要提供的机制是当多个(具体数量等于初始化CountDownLatch时count参数的值)线程都达到了预期状态或完成预期工作时触发事件,其他线程可以等待这个事件来触发自己的后续工作。值得注意的是,CountDownLatch是可以唤醒多个等待的线程的。

通俗点讲用给定的计数 初始化 CountDownLatch设置一个值。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回,调用一次countDown() 计数count -1 , 直到为0时,会通知所有await的线程,注意,这里计数是不能被重置的,也就是只能使用通知一次


为了到达自己预期状态(初始化设置的值)的线程会调用CountDownLatch的countDown方法(该方法会在初始化的值上  count-1),等待的线程会调用CountDownLatch的await方法(当count计数器到达0时调用)。如果CountDownLatch初始化的count值为1,那么这就退化为一个单一事件了,即是由一个线程来通知其他线程,效果等同于对象的wait和notifyAll,count值大于1是常用的方式,目的是为了让多个线程到达各自的预期状态,变为一个事件进行通知,线程则继续自己的行为。

CountDownLatch个重要的方法说明

countDown()

  1. public void countDown()
  2. 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。如果当前计数大于零,则将计数减少。如果新的计数为零,出于线程调度目的,将重新启用所有的等待线程。
  3. 如果当前计数等于零,则不发生任何操作。

await()
  1. public boolean await(long timeout,
  2. TimeUnit unit)
  3. throws InterruptedException
  4. 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。如果当前计数为零,则此方法立刻返回 true 值。如果当前计数大于零,则出于线程调度目的,将禁用当前线程,且在发生以下三种情况之一前,该线程将一直处于休眠状态:
  5. 1.由于调用 countDown() 方法,计数到达零;
  6. 2.或者其他某个线程中断当前线程;
  7. 3.或者已超出指定的等待时间。如果计数到达零,则该方法返回 true 值。

  8. 另外还要注意的几点,如果当前线程:
  9. 1.在进入此方法时已经设置了该线程的中断状态;
  10. 2.或者在等待时被中断,则抛出 InterruptedException,并且清除当前线程的已中断状态。如果超出了指定的等待时间,则返回值为 false。如果该时间小于等于零,则此方法根本不会等待。
  11. 参数:
  12. timeout - 要等待的最长时间
  13. unit - timeout 参数的时间单位。
  14. 返回:
  15. 如果计数到达零,则返回 true;如果在计数到达零之前超过了等待时间,则返回 false
  16. 抛出:
  17. InterruptedException - 如果当前线程在等待时被中断


看一个例子:

private static class WorkThread extends Thread
{
private CountDownLatch cdl;
private int sleepSecond; public WorkThread(String name, CountDownLatch cdl, int sleepSecond)
{
super(name);
this.cdl = cdl;
this.sleepSecond = sleepSecond;
} public void run()
{
try
{
System.out.println(this.getName() + "启动了,时间为" + System.currentTimeMillis());
Thread.sleep(sleepSecond * 1000);
cdl.countDown();
System.out.println(this.getName() + "执行完了,时间为" + System.currentTimeMillis());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
} private static class DoneThread extends Thread
{
private CountDownLatch cdl; public DoneThread(String name, CountDownLatch cdl)
{
super(name);
this.cdl = cdl;
} public void run()
{
try
{
System.out.println(this.getName() + "要等待了, 时间为" + System.currentTimeMillis());
cdl.await();
System.out.println(this.getName() + "等待完了, 时间为" + System.currentTimeMillis());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
} public static void main(String[] args) throws Exception
{
CountDownLatch cdl = new CountDownLatch(3);
DoneThread dt0 = new DoneThread("DoneThread1", cdl);
DoneThread dt1 = new DoneThread("DoneThread2", cdl);
dt0.start();
dt1.start();
WorkThread wt0 = new WorkThread("WorkThread1", cdl, 2);
WorkThread wt1 = new WorkThread("WorkThread2", cdl, 3);
WorkThread wt2 = new WorkThread("WorkThread3", cdl, 4);
wt0.start();
wt1.start();
wt2.start();
}

看一下运行结果:

DoneThread2要等待了, 时间为1444563077434
DoneThread1要等待了, 时间为1444563077434
WorkThread1启动了,时间为1444563077434
WorkThread3启动了,时间为1444563077435
WorkThread2启动了,时间为1444563077435
WorkThread1执行完了,时间为1444563079435
WorkThread2执行完了,时间为1444563080435
WorkThread3执行完了,时间为1444563081435
DoneThread1等待完了, 时间为1444563081435
DoneThread2等待完了, 时间为1444563081435

效果十分明显,解释一下:

1、启动2个线程DoneThread线程等待3个WorkThread全部执行完

2、3个WorkThread全部执行完,最后执行完的WorkThread3执行了秒符合预期

3、后三句从时间上看几乎同时出现,说明CountDownLatch设置为3,WorkThread3执行完,两个wait的线程马上就执行后面的代码了

这相当于是一种进化版本的等待/通知机制,它可以的实现的是多个工作线程完成任务后通知多个等待线程开始工作,之前的都是一个工作线程完成任务通知一个等待线程或者一个工作线程完成任务通知所有等待线程。


使用场景:

在一些应用场合中,需要等待某个条件达到要求后才能做后面的事情;同时当线程都完成后也会触发事件,以便进行后面的操作。 这个时候就可以使用CountDownLatch。CountDownLatch最重要的方法是countDown()和await(),前者主要是倒数一次,后者是等待倒数到0,如果没有到达0,就只有阻塞等待了。


CountDownLatch其实是很有用的,特别适合这种将一个问题分割成N个部分的场景,所有子部分完成后,通知别的一个/几个线程开始工作。比如我要统计C、D、E、F盘的文件,可以开4个线程,分别统计C、D、E、F盘的文件,统计完成把文件信息汇总到另一个/几个线程中进行处理

参考另一个实例

  1. public class CountDownLatchTest {
  2. // 模拟了100米赛跑,10名选手已经准备就绪,只等裁判一声令下。当所有人都到达终点时,比赛结束。
  3. public static void main(String[] args) throws InterruptedException {
  4. // 开始的倒数锁
  5. final CountDownLatch begin = new CountDownLatch(1);
  6. // 结束的倒数锁
  7. final CountDownLatch end = new CountDownLatch(10);
  8. // 十名选手
  9. final ExecutorService exec = Executors.newFixedThreadPool(10);
  10. for (int index = 0; index < 10; index++) {
  11. final int NO = index + 1;
  12. Runnable run = new Runnable() {
  13. public void run() {
  14. try {
  15. // 如果当前计数为零,则此方法立即返回。
  16. // 等待
  17. begin.await();
  18. Thread.sleep((long) (Math.random() * 10000));
  19. System.out.println("No." + NO + " arrived");
  20. } catch (InterruptedException e) {
  21. } finally {
  22. // 每个选手到达终点时,end就减一
  23. end.countDown();
  24. }
  25. }
  26. };
  27. exec.submit(run);
  28. }
  29. System.out.println("Game Start");
  30. // begin减一,开始游戏
  31. begin.countDown();
  32. // 等待end变为0,即所有选手到达终点
  33. end.await();
  34. System.out.println("Game Over");
  35. exec.shutdown();
  36. }
  37. }

输出结果

  1. Game Start
  2. No.9 arrived
  3. No.6 arrived
  4. No.8 arrived
  5. No.7 arrived
  6. No.10 arrived
  7. No.1 arrived
  8. No.5 arrived
  9. No.4 arrived
  10. No.2 arrived
  11. No.3 arrived
  12. Game Over

可以看得出来 CountDownLatch 还是很有用的,在某些场合,一部分线程完成 之后 通知另一部分线程 , 更加灵活

java 多线程 27 :多线程组件之CountDownLatch的更多相关文章

  1. Java多线程20:多线程下的其他组件之CountDownLatch、Semaphore、Exchanger

    前言 在多线程环境下,JDK给开发者提供了许多的组件供用户使用(主要在java.util.concurrent下),使得用户不需要再去关心在具体场景下要如何写出同时兼顾线程安全性与高效率的代码.之前讲 ...

  2. java并发与多线程面试题与问题集合

    http://www.importnew.com/12773.html     https://blog.csdn.net/u011163372/article/details/73995897    ...

  3. Java中使用多线程、curl及代理IP模拟post提交和get访问

    Java中使用多线程.curl及代理IP模拟post提交和get访问 菜鸟,多线程好玩就写着玩,大神可以路过指教,小弟在这受教,谢谢! 更多分享请关注微信公众号:lvxing1788 ~~~~~~ 分 ...

  4. Java中使用多线程、curl及代理IP模拟post提交和get訪问

    Java中使用多线程.curl及代理IP模拟post提交和get訪问 菜鸟,多线程好玩就写着玩.大神能够路过不吝赐教.小弟在这受教.谢谢! 很多其它分享请关注微信公众号:lvxing1788 ~~~~ ...

  5. Java中的多线程技术全面详解

    本文主要从整体上介绍Java中的多线程技术,对于一些重要的基础概念会进行相对详细的介绍,若有叙述不清晰或是不正确的地方,希望大家指出,谢谢大家:) 为什么使用多线程 并发与并行 我们知道,在单核机器上 ...

  6. 【转】Java中的多线程学习大总结

    多线程作为Java中很重要的一个知识点,在此还是有必要总结一下的. 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程 ...

  7. 关于java基础、多线程、JavaWeb基础、数据库、SSM、Springboot技术汇总

    作者 : Stanley 罗昊 本人自行总结,纯手打,有疑问请在评论区留言 [转载请注明出处和署名,谢谢!] 一.java基础 1.多态有哪些体现形式? 重写.重载 2. Overriding的是什么 ...

  8. 程序员Java架构师多线程面试题和回答解析

    当我们在Java架构师面试的过程中常见的多线程和并发方面的问题肯定是必不可少的一部分.那么在面试之前我们更应该多准备一些关于多线程方面的问题. 面试官只是想确信面试者有足够的Java线程与并发方面的知 ...

  9. Java基础技术多线程与并发面试【笔记】

    Java基础技术多线程与并发 什么是线程死锁? ​死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去,我们就可以称 ...

随机推荐

  1. 【DeepLearning】UFLDL tutorial错误记录

    (一)Autoencoders and Sparsity章节公式错误: s2 应为 s3. 意为从第2层(隐藏层)i节点到输出层j节点的误差加权和. (二)Support functions for ...

  2. Linux下Valgrind的使用概述 来源:Linux社区 作者:dndxhej

    本地使用情况,在Ubantu系统上编写的测试性能: 1,下载:sudo apt-get install valgrind; 2,验证是否安装成功:试着valgrind ls -l来检测是否正常工作: ...

  3. zabbix v3.0安装部署

    这篇文章没有写明init的部分要注意 zabbix v3.0安装部署 摘要: 本文的安装过程摘自http://www.ttlsa.com/以及http://b.lifec-inc.com ,和站长凉白 ...

  4. uploadify.js参数说明(转)

    一.属性 属性名称 默认值 说明 auto true 设置为true当选择文件后就直接上传了,为false需要点击上传按钮才上传 . buttonClass ” 按钮样式 buttonCursor ‘ ...

  5. C# 自定义控件,日期时间选择输入插件

    权声明:本文为博主原创文章,未经博主允许不得转载. // 为textBox1添加一个日期时间选择控件 DateTimeChoser.AddTo(textBox1); DateTimeChoser.De ...

  6. Swift 对象

    1.对象 对象是类的具体化的东西,从抽象整体中具体化出的特定个体. 对象是一个动态的概念. 每一个对象都存在着有别于其他对象的属于自己的独特属性和行为. 对象的属性可以随着他自己的行为的变化而改变. ...

  7. android View未渲染时获得高度

    ViewTreeObserver vto = tv.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new OnGlobalLayoutLis ...

  8. Android MediaPlayer和VideoView的使用

          MediaPlayer MediaPlayer类是Androd多媒体框架中的一个重要组件,通过该类,我们可以以最小的步骤来获取,解码和播放音视频.它支持三种不同的媒体来源: 本地资源 内部 ...

  9. Asp.net GridView转换成DataTable

    GridView绑定DataTable后,如何获取GridView绑定后显示的值,在项目需求需要的背景下,搜索了获取单元格显示文本的方法,然后写了一个静态方法,经过在项目中的使用,bug的修复,较为稳 ...

  10. 基于Android平台的会议室管理系统具体设计说明书

    会议室管理系统具体设计说明书 第一部分  引言 1.编写目的 本说明对会议室管理系统项目的各模块.页面.脚本分别进行了实现层面上的要求和说明. 软件开发小组的产品实现成员应该阅读和參考本说明进行代码的 ...