CountDownLatch(门闩)和CyclicBarrier(栅栏)都是java.concurrent里的多线程控制工具类,我这里说他们两个都是控制线程等待,它们都像是一个计数器,让某一个线程等待计数完成后再执行,但是又有些不同,CountDownLatch是通过计数完成后才执行线程来控制线程等待,而CyclicBarrier可以是线程执行到某一处后等待计数,计数完成后再继续执行。

简单来说,CountDownLatch控制线程等待,是在做好所有准备后,才执行线程,就像早上起来我们会刷牙洗脸收拾好所有东西吃完早餐再出门一样。CyclicBarrier则不是“整装待发”后再出门,你可能洗漱好收拾好所有东西后便出门了,然后等待去到面包店买了面包再吃早餐。我感觉CyclicBarrier才是真正的让线程等待。

CountDownLatch使用起来比较简单,构造函数里只有一个参数就是设置这个计数器的计数个数。

Public CountDownLatch(int count)

package countdownlatch;import java.util.concurrent.CountDownLatch;
import java.util.Random;public class CountDownLatchDemo implements Runnable {static final CountDownLatch CDL = new CountDownLatch(10); //计数数量为10static final CountDownLatchDemo CDLdemo = new CountDownLatchDemo();@Overridepublic void run() {try {Thread.sleep(new Random().nextInt(10)*1000);System.out.println(Thread.currentThread().getName()+" complete.");CDL.countDown(); //计数器减一} catch(InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) throws InterruptedException {Thread arr[] = new Thread[10];for(int i=0; i<10; i++) {arr[i] = new Thread(CDLdemo);arr[i].start();}//计数器等待,直到倒计时完成CDL.await();System.out.println("All Threads complete.");}}

代码第8行实例化一个CountDownLatch计数器,计数值传入10,即需要等待10个线程完成任务后,在CountDownLatch上等待的线程才能执行,代码第16行,每当一个线程完成自己的任务后,就调用CountDownLatch的countDown( )方法,表示让CountDownLatch的计数值减1。主线程中则调用CountDownLatch的await( )方法表示要求主线程在CountDownLatch上等待,等待CountDownLatch中的所有任务执行完成后,再继续执行自己的任务。

到循环栅栏CyclicBarrier,同样可以实现计数让线程之间等待,为什么叫循环栅栏,因为CyclicBarrier实例化后可以循环使用,假设我们设置了计数值为10,那么在计数值达到10后,会自动清零,可以继续使用。CyclicBarrier的构造函数有两个参数:

Public CyclicBarrier(int parties, Runnable barrierAction)

第一个参数parties表示计数值,第二个参数barrierAction表示计数值达到后执行的动作,这样明显比CountDownLatch好一些。来看看CyclicBarrier具体怎么用:

package cyclicbarrier;import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.Random;public class CyclicBarrierDemo {public static class Student implements Runnable {//实例化循环栅栏private final CyclicBarrier cyclic;//构造函数初始化Student(CyclicBarrier cyclic) {this.cyclic = cyclic;}//模拟线程到达void ThreadReady() {try {Thread.sleep(Math.abs(new Random().nextInt()%10000));} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+" ready.");}//模拟线程工作void ThreadStart() {try {Thread.sleep(Math.abs(new Random().nextInt()%10000));} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+" complete");}@Overridepublic void run() {try {ThreadReady(); //所有线程开始到达cyclic.await(); //等待所有线程到达ThreadStart(); //所有线程开始运行cyclic.await(); //等待所有线程完成} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}}public static class BarrierRun implements Runnable {boolean flag;int i;//构造函数初始化BarrierRun(boolean flag, int i) {this.flag = flag;this.i = i;}@Overridepublic void run() {if(flag) {System.out.println("Thread "+i+"个,所有线程完成.");} else {System.out.println("Thread "+i+"个,线程到达.");flag = true;}}}public static void main(String[] args) {boolean flag = false;int i =10;Thread arr[] = new Thread[i];BarrierRun br = new BarrierRun(flag, i);/*CyclicBarrier的构造函数:public CyclicBarrier(int parties, Runnable barrierAction)* 第一个参数parties是计数个数,第二个参数是当计数器一次计数完成后,系统会执行的动作.*/CyclicBarrier cyclic = new CyclicBarrier(i, br);for(int j=0; j<i; j++) {arr[j] = new Thread(new Student(cyclic));arr[j].start();}}}

首先第9到50行是我们模拟线程要做的事,第18到26行,线程睡眠随机时间模拟不同线程的不同到达时间,第27到35行同样线程睡眠随机时间来模拟不同线程完成任务的消耗时间。到了第40行,线程到达后栅栏cyclic就调用await()方法,意思是线程到这里后边在CyclicBarrier上等待,等到所有线程到达后,才继续执行。当所有线程到达后,便继续往下,到第42行模拟线程完成任务,每个线程完成任务后都会再次进入第43行的cyclic.await()等待,意指等待所有线程完成任务。

我们先继续往下看,第52到70行的BarrierRun函数是用来传入CyclicBarrier的第二个参数,即当CyclicBarrier完成一次完整计数后系统要执行的动作,这里BarrierRun函数要做的就是当cyclic计数完成后根据输出相应的提示语句,当flag为true时提示所有线程完成任务,false时提示所有线程到达完毕。

一切准备就绪后就来看主线程,第76行实例化一个BarrierRun对象br,传入参数false和线程数i,然后第80行实例化一个CyclicBarrier对象cyclic,传入参数i是计数值,br即计数完成后要执行的动作。接着让10个线程陆续执行即可,每个线程中都会“被”cyclic等待两次,第一次是等待10个线程到达,10个线程都到达后,执行cyclic的执行动作BarrierRun,也就是输出线程到达提示,接着线程继续执行,随机时间睡眠后,会继续进入cyclic等待,等待所有线程的执行完毕后,输出语句。

仔细观察的你可能会发现代码第44行开始要抛出两个异常:

第一个异常InterruptedException就是说在等待过程中线程被中断,按我在书上看到的说法:“大部分迫使线程等待的方法都可能会抛出这个异常,使得线程在等待时依然可以响应外部紧急事件。”第二个异常BrokenBarrierException,这个异常表示当前的CyclicBarrier已经破损,按书上说法:“如果继续等待,可能是徒劳无功的,因为可能系统已经没有办法等待所有线程到期了。“遇到这个异常如果不抛出,可能会现入无止境的等待中。就像线程睡眠时一定要抛出异常一样,使用CyclicBarrier等待线程记得一定要抛出BrokenBarrierException异常。

完整代码已上传GitHub:

https://gitee.com/justinzeng/codes/fy4whmn2u3qgpr0lsoxac21

https://github.com/justinzengtm/Multithreading/blob/master/CyclicBarrierDemo.java

两种线程“等待”,“门闩”和“栅栏”相关推荐

  1. Android AsyncTask两种线程池分析和总结

    转自:http://bbs.51cto.com/thread-1114378-1-1.html Android AsyncTask两种线程池分析和总结 (一)    前言 在android Async ...

  2. Android之AsyncTask两种线程池分析和总结

    Android AsyncTask两种线程池分析和总结 (一)    前言 在android AsyncTask里面有两种线程池供我们调用 1.    THREAD_POOL_EXECUTOR, 异步 ...

  3. java中创建两种线程的方式_java中创建线程的两种方式有什么区别?

    *** 一.创建线程 1.继承Thread类 定义类继承Thread, 重写run()方法, 将线程中要执行的代码写在run()方法中 创建该类对象, 调用start()方法就可以开启一条新线程, 新 ...

  4. 一篇比较好的介绍(两种线程模式)

    http://blog.joycode.com/peon/archive/2007/05/13/102457.aspx

  5. Android 线程 thread 两种实现方法!

    这篇文章中有三点需要提前说明一下, 一: 在android中有两种实现线程thread的方法: 一种是,扩展java.lang.Thread类 另一种是,实现Runnable接口 二: Thread类 ...

  6. Java多线程系列(五):线程池的实现原理、优点与风险、以及四种线程池实现

    为什么需要线程池 我们有两种常见的创建线程的方法,一种是继承Thread类,一种是实现Runnable的接口,Thread类其实也是实现了Runnable接口.但是我们创建这两种线程在运行结束后都会被 ...

  7. winform判断线程有没有完成_并发编程系列1:线程池的架构实现、大小配置、及四种线程池使用...

    △ 公众号回复关键词"架构" 即可领取<1500+BAT架构及面试专题合集> 本篇为线程池系列文章之一,不经常使用线程池的童鞋,还有对几种线程的使用不甚了解的童鞋,可以 ...

  8. 项目使用线程池_并发编程系列1:线程池的架构实现、大小配置、及四种线程池使用...

    △ 公众号回复关键词"架构" 即可领取<1500+BAT架构及面试专题合集> 本篇为线程池系列文章之一,不经常使用线程池的童鞋,还有对几种线程的使用不甚了解的童鞋,可以 ...

  9. .NET一个线程更新另一个线程的UI(两种实现方法及若干简化)

    本片博文接上一篇:.NET多线程执行函数,给出实现一个线程更新另一个线程UI的两种方法. Winform中的控件是绑定到特定的线程的(一般是主线程),这意味着从另一个线程更新主线程的控件不能直接调用该 ...

最新文章

  1. 插值算法C实现(一元全区间)
  2. 使用CSS对页面加载的淡入效果
  3. mysql root情况
  4. css3中transform的用法
  5. 【拔刀吧少年】之shell数组
  6. commons-fileupload实现文件上传下载
  7. linux下计算目录文件和,统计Linux 中文件和文件夹/目录的数量(示例代码)
  8. HP 1218 无线设置
  9. wine mac 中文 方块乱码 解决
  10. apache druid 与kafka整合使用
  11. Maven 单元测试报错:错误: 找不到或无法加载主类 @{failsafeArgLine}
  12. 阅读 深入理解JVM虚拟机笔记一
  13. 深入理解计算机系统寄存器寻址讲解
  14. 初学爬虫,简单爬取必应壁纸
  15. java反序列化漏洞POP查找_Laravel8反序列化POP链分析挖掘
  16. 【云原生之Docker实战】使用Docker部署siyuan个人笔记系统
  17. 数学建模之差分方程模型详解
  18. day25 Scala编cala编译器安装 3.1. 安装JDK 因为Scala是运行在JVM平台上的,所以安装Scala之前要安装JDK 3.2. 安装Scala 3.2.1. Windows基础
  19. 安装SQL server显示重新启动计算机失败解决方法
  20. 成才之路杂志社成才之路编辑部成才之路杂志2022年第33期目录

热门文章

  1. 使用Vibrator震动器提示
  2. 宠物商店项目_充分利用宠物项目的7个技巧
  3. 【Python爬虫+数据可视化】国内疫情或将零增长,我们离疫情结束有多远?(世界地图)
  4. win10本地连接不见了(手机连不上网怎么回事)
  5. 基于ssm框架的足球队俱乐部管理系统
  6. 瞄准高消费人群抓取消费数据
  7. [python] 运算顺序
  8. 历史上十大杰出的皇帝
  9. 启动腾讯软件出现 应用程序无法启动并行配置不正确 解决
  10. 与MQTT的初定情缘