业务场景描述:假设一条流水线上有三个工作者:worker1,worker2,worker3。有一个任务的完成需要他们三者协作完成,worker3可以开始这个任务的前提是worker1和worker2完成了他们的工作,而worker1和worker2是可以并行他们各自的工作的。

1.join实现

public class CountDownLatchAndJoin {public static void main(String[] args) throws InterruptedException {// 三个独立的工人线程worker worker1 = new worker("worker1", (long) (Math.random()*4000));worker worker2 = new worker("worker2", (long) (Math.random()*4000));worker worker3 = new worker("worker3", (long) (Math.random()*4000));
//        worker worker1 = new worker("worker1", 6000);
//        worker worker2 = new worker("worker2", 5000);
//        worker worker3 = new worker("worker3", 5000);
        worker1.start();worker2.start();worker1.join();worker2.join();System.out.println("准备工作就绪...");worker3.start();}// 工人类public static class worker extends Thread {// 名字private String name;//工作时间private long time;worker(String name, long time) {this.name = name;this.time = time;}public void run() {try {System.out.println(name + "开始工作");Thread.sleep(time);System.out.println(name + "工作完成,耗费时间=" + time);} catch (InterruptedException e) {e.printStackTrace();}}}}

结果:

worker2开始工作
worker1开始工作
worker1工作完成,耗费时间=601
worker2工作完成,耗费时间=2886
准备工作就绪...
worker3开始工作
worker3工作完成,耗费时间=686

可以顺利的完成工作,join的工作原理是,不停检查thread是否存活,如果存活则让当前线程永远wait,直到thread线程终止,线程的notifyAll就会被调用,还可以理解为join就是插队的意思


2.CountDownLatch实现

public class CountDownLatchTest {public static void main(String[] args) throws InterruptedException {// 初始化计数器为2CountDownLatch countDownLatch = new CountDownLatch(2);// 三个独立的工人线程worker worker1 = new worker("worker1", (long) (Math.random() * 4000), countDownLatch);worker worker2 = new worker("worker2", (long) (Math.random() * 4000), countDownLatch);worker worker3 = new worker("worker3", (long) (Math.random() * 4000), countDownLatch);worker1.start();worker2.start();// 当计数器不为0的时候均等待
        countDownLatch.await();System.out.println("准备工作就绪...");worker3.start();}public static class worker extends Thread {private String name;private long time;private CountDownLatch countDownLatch;worker(String name, long time, CountDownLatch countDownLatch) {this.name = name;this.time = time;this.countDownLatch = countDownLatch;}public void run() {System.out.println(name + " 开始工作了。。。");// 减一
            countDownLatch.countDown();System.out.println(name + " 工作完成。。。");}}
}

创建一个计数器为2的 CountDownLatch ,让Worker持有这个CountDownLatch 实例,当完成自己的工作后,调用countDownLatch.countDown() 方法将计数器减1。countDownLatch.await() 方法会一直阻塞直到计数器为0,主线程才会继续往下执行。

运行结果:

worker1 开始工作了。。。
worker1 工作完成。。。
worker2 开始工作了。。。
worker2 工作完成。。。
准备工作就绪...
worker3 开始工作了。。。
worker3 工作完成。。。

从结果上来看,都解决了问题,但是他们的区别在哪里呢?

业务场景:假设worker的工作可以分为两个阶段,work3 只需要等待work1和work2完成他们各自工作的第一个阶段之后就可以开始自己的工作了,而不是场景1中的必须等待work1和work2把他们的工作全部完成之后才能开始。这样join就不可以实现了,应当采用CountDownLatch 来实现。

public class CountDownLatchTest {public static void main(String[] args) throws InterruptedException {// 初始化计数器为5CountDownLatch countDownLatch = new CountDownLatch(5);// 三个独立的工人线程worker worker1 = new worker("worker1", (long) (Math.random() * 4000), countDownLatch);worker worker2 = new worker("worker2", (long) (Math.random() * 4000), countDownLatch);worker worker3 = new worker("worker3", (long) (Math.random() * 4000), countDownLatch);worker worker4 = new worker("worker4", (long) (Math.random() * 4000), countDownLatch);worker worker5 = new worker("worker5", (long) (Math.random() * 4000), countDownLatch);worker worker6 = new worker("worker6", (long) (Math.random() * 4000), countDownLatch);worker1.start();worker2.start();worker3.start();worker4.start();worker5.start();// 当计数器不为0的时候均等待
        countDownLatch.await();System.out.println("准备工作就绪...");worker6.start();}public static class worker extends Thread {private String name;private long time;private CountDownLatch countDownLatch;worker(String name, long time, CountDownLatch countDownLatch) {this.name = name;this.time = time;this.countDownLatch = countDownLatch;}public void run() {try {System.out.println(name + " 工作开始。。。");Thread.sleep(time);System.out.println(name + " 第一阶段工作完成。。。用时:" + time);// 计数器减一
                countDownLatch.countDown();// 假设第二阶段的工作都需要两秒完成Thread.sleep(2000);System.out.println(name + " 第二阶段工作完成。。。用时:" + (time + 2000));} catch (InterruptedException e) {e.printStackTrace();}}}
}

多运行几次发现:线程6等到前面5个线程的第一阶段全部完成,就开始运行了,运行结果:

worker3 工作开始。。。
worker2 工作开始。。。
worker1 工作开始。。。
worker4 工作开始。。。
worker5 工作开始。。。
worker3 第一阶段工作完成。。。用时:1410
worker5 第一阶段工作完成。。。用时:2022
worker2 第一阶段工作完成。。。用时:2273
worker1 第一阶段工作完成。。。用时:2856
worker3 第二阶段工作完成。。。用时:3410
worker4 第一阶段工作完成。。。用时:3430
准备工作就绪...
worker6 工作开始。。。
worker5 第二阶段工作完成。。。用时:4022
worker2 第二阶段工作完成。。。用时:4273
worker1 第二阶段工作完成。。。用时:4856
worker4 第二阶段工作完成。。。用时:5430
worker6 第一阶段工作完成。。。用时:3773
worker6 第二阶段工作完成。。。用时:5773

总结:调用thread.join() 方法必须等thread 执行完毕,当前线程才能继续往下执行,而CountDownLatch通过计数器提供了更灵活的控制,只要检测到计数器为0当前线程就可以往下执行而不用管相应的thread是否执行完毕。

转载于:https://www.cnblogs.com/zhangjianbing/p/9087841.html

并发编程大师系列之:CountDownLatch和Join相关推荐

  1. 『死磕Java并发编程系列』并发编程工具类之CountDownLatch

    <死磕 Java 并发编程>系列连载中,大家可以关注一波:

  2. 并发编程JUC系列及部分问题

    什么是 CAS 吗? CAS(Compare And Swap)指比较并交换.CAS算法CAS(V, E, N)包含 3 个参数,V 表示要更新的变量,E 表示预期的值,N 表示新值.在且仅在 V 值 ...

  3. 突击并发编程JUC系列-ReentrantReadWriteLock

    突击并发编程JUC系列演示代码地址: https://github.com/mtcarpenter/JavaTutorial 本章节将学习 ReentrantReadWriteLock(读写锁),Re ...

  4. java lock 对象_Java并发编程锁系列之ReentrantLock对象总结

    Java并发编程锁系列之ReentrantLock对象总结 在Java并发编程中,根据不同维度来区分锁的话,锁可以分为十五种.ReentranckLock就是其中的多个分类. 本文主要内容:重入锁理解 ...

  5. 多线程 可参考 博主【 lx青萍之末】 的 【C++并发编程 】系列博客

    关于多线程 相关知识 可参考博主[ lx青萍之末] 的 [C++并发编程 ]系列博客 https://blog.csdn.net/daaikuaichuan/category_6887432.html

  6. Java并发编程工具类:CountDownLatch、CyclicBarrier、Semaphore

    在jdk5中,java提供了一些非常有用的辅助工具类,包括CountDownLatch和CyclicBarrier(两者都可以实现线程之间的通信).Semaphore(控制方法被线程访问的数量),他们 ...

  7. java大师_著名的Java并发编程大师都这么说了,你还不知道伪共享么!

    记得关注我,订阅更多好文!全文共计2163字18图,预计阅读时间13分钟 大家好,我是tin,这是我的第7篇原创文章 图拍摄于深圳桃园南山图书馆,年前某个阳光明媚的周六,看到挂满的灯笼,觉得甚是喜庆. ...

  8. java骂人_著名的Java并发编程大师都这么说了,你还不知道伪共享么!

    记得关注我[看点代码再上班],订阅更多好文! 全文共计2163字18图,预计阅读时间13分钟 大家好,我是tin,这是我的第7篇原创文章 WechatIMG43.jpeg 图拍摄于深圳桃园南山图书馆, ...

  9. redis深度历险 pdf_程序员面试必备精选文档:Redis+Ng+Tomcat+并发编程+Spring系列

    前言 相信好多程序员都想在金三银四的时候找到或者跳槽到自己心仪的大厂.但是,今年的错过了,学习的脚步是不能停下的,这样才会抓住每一次机会,希望大家都可以找到自己心仪的公司就业.当然,除了做项目来提高自 ...

  10. 并发编程陷阱系列(八)不要吞食CountDownLatch的线程异常

    之前的文章中已经介绍了无处不在的InterruptedException的处理方式了,使用CountDownLatch也会有类似的问题(正确的处理方式见下面代码: Thread.currentThre ...

最新文章

  1. ios 中的小技巧 - 总有你想要的 一
  2. C#中获取指定路径下特定开头和后缀的所有文件
  3. python打印二进制内容,Python字节不打印二进制
  4. 改进MySQL Order By Rand()的低效率
  5. 老李谈HTTP1.1的长连接
  6. partial is not defined的解决办法
  7. 动态规划(装配线调度)
  8. linux内核源码 -- list链表
  9. 关闭 启动_win10系统关闭快速启动功能教程
  10. Mongodb笔记(三)user aggregate mapReduce
  11. 原创:协同过滤之ALS
  12. cisco 的端口聚合
  13. 毕设题目:Matlab图像评价
  14. luogu_4551【题解】最长异或路径 trie树
  15. Centos 安装libreoffice 以及 word转pdf转html转epub转txt
  16. 基数排序组合桶式排序
  17. python根据题库答案自动答题_python实现百万答题自动百度搜索答案
  18. 配置MySQL环境、安装MySQL、解决mysql无法修改密码问题
  19. 用python绘制熊猫图案_在python中绘制熊猫系列的CDF
  20. 天是岸:优秀的网络营销推广人,身上一般都具备这7大特质

热门文章

  1. MySQL主从同步(五)——排错思路
  2. HDLM for AIX安装
  3. turtle模块实现多边形
  4. 调用新浪微博显示用户信息
  5. [安卓学习]AndroidManifest.xml文件内容详解
  6. cpu高 rust腐蚀_木器漆如何选购,Rust-Oleum户外防水防腐木器漆怎么样?
  7. 如何卸载eclipse?
  8. python中安装decimal模块_第38天:Python decimal 模块
  9. deebot地面清洁机器人怎么关_买扫地机器人还是吸尘器?看完你就明白了
  10. 再谈UDP协议—浅入理解深度记忆