先上结论

原理

  • join 原理:在当前线程中调用另一个线程线程 thread 的 join() 方法时,会调用该 thread 的 wait() 方法,直到这个 thread 执行完毕(JVM在 run() 方法执行完后调用 exit() 方法,而 exit() 方法里调用了 notifyAll() 方法)会调用 notifyAll() 方法主动唤醒当前线程。

源码如下:

    public final void join() throws InterruptedException {join(0);}/***  注意这个方法是同步的 */public final synchronized void join(long millis)throws InterruptedException {long base = System.currentTimeMillis();long now = 0;if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}/***  join方法默认参数为0,会直接阻塞当前线程*/if (millis == 0) {while (isAlive()) {wait(0);}} else {while (isAlive()) {long delay = millis - now;if (delay <= 0) {break;}wait(delay);now = System.currentTimeMillis() - base;}}}public final native boolean isAlive();}
  • countDownLatch 原理:可以理解为一个计数器。在初始化 CountDownLatch 的时候会在类的内部初始化一个int的变量,每当调用 countDownt() 方法的时候这个变量的值减1,而 await() 方法就是去判断这个变量的值是否为0,是则表示所有的操作都已经完成,否则继续等待。

源码如下(源码比较少,直接全贴出来了,所有中文注释是我自己加上去的):

public static class CountDownLatch {private static final class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 4982264981922014374L;/*** 初始化state*/Sync(int count) {setState(count);}int getCount() {return getState();}/*** 尝试获取同步状态*     只有当同步状态为0的时候返回1*/protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}/*** 自旋+CAS的方式释放同步状态*/protected boolean tryReleaseShared(int releases) {// Decrement count; signal when transition to zerofor (;;) {int c = getState();if (c == 0)return false;int nextc = c-1;if (compareAndSetState(c, nextc))return nextc == 0;}}}private final Sync sync;/***  初始化一个同步器*/public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);}/*** 调用同步器的acquireSharedInterruptibly方法,并且是响应中断的*/public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);}/*** 调用同步器的releaseShared方法去让state减1*/public void countDown() {sync.releaseShared(1);}/*** 获取剩余的count*/public long getCount() {return sync.getCount();}public String toString() {return super.toString() + "[Count = " + sync.getCount() + "]";}}

区别及注意事项

  • join和countDownLatch都能实现让当前线程阻塞等待其他线程执行完毕,join使用起来更简便,不过countDownLatch粒度更细。
  • 由于CountDownLatch需要开发人员很明确需要等待的条件,否则容易造成await()方法一直阻塞。

如何使用

  • 一个简单的小例子
public class Test {private static final Logger logger = LoggerFactory.getLogger(Test.class);public static void main(String[] args) {long sleepTime = 5000;try {TestJoinThread joinThread1 = new TestJoinThread("joinThread1",sleepTime);TestJoinThread joinThrad2 = new TestJoinThread("joinThrad2",sleepTime);joinThread1.start();joinThrad2.start();joinThread1.join();joinThrad2.join();logger.info("主线程开始运行...");} catch (InterruptedException e) {logger.error("test join err!",e);}try {CountDownLatch count = new CountDownLatch(2);TestCountDownLatchThread countDownLatchThread1 = new TestCountDownLatchThread(count,"countDownLatchThread1",sleepTime);TestCountDownLatchThread countDownLatchThread2 = new TestCountDownLatchThread(count,"countDownLatchThread2",sleepTime);countDownLatchThread1.start();countDownLatchThread2.start();count.await();logger.info("主线程开始运行...");} catch (InterruptedException e) {logger.error("test countDownLatch err!",e);}}static class TestJoinThread extends Thread{private String threadName;private long sleepTime;public TestJoinThread(String threadName,long sleepTime){this.threadName = threadName;this.sleepTime = sleepTime;}@Overridepublic void run() {try{logger.info(String.format("线程[%s]开始运行...",threadName));Thread.sleep(sleepTime);logger.info(String.format("线程[%s]运行结束 耗时[%s]s",threadName,sleepTime/1000));}catch (Exception e){logger.error("TestJoinThread run err!",e);}}}static class TestCountDownLatchThread extends Thread{private String threadName;private long sleepTime;private CountDownLatch countDownLatch;public TestCountDownLatchThread(CountDownLatch countDownLatch,String threadName,long sleepTime){this.countDownLatch = countDownLatch;this.threadName = threadName;this.sleepTime = sleepTime;}@Overridepublic void run() {try{logger.info(String.format("线程[%s]开始运行...",threadName));Thread.sleep(sleepTime);logger.info(String.format("线程[%s]运行结束 耗时[%s]s",threadName,sleepTime/1000));countDownLatch.countDown();}catch (Exception e){logger.error("TestCountDownLatchThread run err!",e);}}}
}

日志输出:

11:18:01.985 [Thread-1] INFO com.sync.Test - 线程[joinThrad2]开始运行...
11:18:01.985 [Thread-0] INFO com.sync.Test - 线程[joinThread1]开始运行...
11:18:06.993 [Thread-1] INFO com.sync.Test - 线程[joinThrad2]运行结束...耗时[5]s
11:18:06.993 [Thread-0] INFO com.sync.Test - 线程[joinThread1]运行结束...耗时[5]s
11:18:06.993 [main] INFO com.sync.Test - 主线程开始运行...
11:18:06.995 [Thread-2] INFO com.sync.Test - 线程[countDownLatchThread1]开始运行...
11:18:06.995 [Thread-3] INFO com.sync.Test - 线程[countDownLatchThread2]开始运行...
11:18:11.996 [Thread-2] INFO com.sync.Test - 线程[countDownLatchThread1]运行结束...耗时[5]s
11:18:11.996 [Thread-3] INFO com.sync.Test - 线程[countDownLatchThread2]运行结束...耗时[5]s
11:18:11.996 [main] INFO com.sync.Test - 主线程开始运行...

可以看到:joinThread1 和 joinThread2 同时开始执行,5s后主线程开始执行。countDownLatchThread1 和 countDownLatchThread2 也是一样的效果。

那么我上面所说的粒度更细有怎样的应用场景呢?

我对 TestCountDownLatchThread类 的 run() 方法做一点小改动:

@Override
public void run() {try{logger.info(String.format("线程[%s]第一阶段开始运行...",threadName);Thread.sleep(sleepTime);logger.info(String.format("线程[%s]第一阶段运行结束耗时[%s]s",threadName,sleepTime/1000));countDownLatch.countDown();logger.info(String.format("线程[%s]第二阶段开始运行...",threadName);Thread.sleep(sleepTime);logger.info(String.format("线程[%s]第二阶段运行结束耗时[%s]s",threadName,sleepTime/1000));}catch (Exception e){logger.error("TestCountDownLatchThread run err!",e);}
}

这个时候日志输出会变成这样:

12:59:35.912 [Thread-1] INFO com.sync.Test - 线程[countDownLatchThread2]第一阶段开始运行...
12:59:35.912 [Thread-0] INFO com.sync.Test - 线程[countDownLatchThread1]第一阶段开始运行...
12:59:40.916 [Thread-0] INFO com.sync.Test - 线程[countDownLatchThread1]第一阶段运行结束 耗时[5]s
12:59:40.916 [Thread-1] INFO com.sync.Test - 线程[countDownLatchThread2]第一阶段运行结束 耗时[5]s
12:59:40.916 [main] INFO com.sync.Test - 主线程开始运行...
12:59:40.916 [Thread-0] INFO com.sync.Test - 线程[countDownLatchThread1]第二阶段开始运行...
12:59:40.916 [Thread-1] INFO com.sync.Test - 线程[countDownLatchThread2]第二阶段开始运行...
12:59:45.917 [Thread-0] INFO com.sync.Test - 线程[countDownLatchThread1]第二阶段运行结束 耗时[5]s
12:59:45.917 [Thread-1] INFO com.sync.Test - 线程[countDownLatchThread2]第二阶段运行结束 耗时[5]s

也就是说如果当前线程只需要等待其他线程一部分任务执行完毕的情况下就可以用 countDownLatch 来实现了,而 join 则实现不了这种粒度的控制。

转载于:https://www.cnblogs.com/hechao123/p/9533653.html

join和countDownLatch原理及区别详解相关推荐

  1. oracle join详解,inner join和left join之间的区别详解

    前言 关于inner join 与 left join 之间的区别,以前以为自己搞懂了,今天从前端取参数的时候发现不是预想中的结果,才知道问题出在inner join 上了. 需求是从数据库查数据,在 ...

  2. Spark SQL原理及常用方法详解(二)

    Spark SQL 一.Spark SQL基础知识 1.Spark SQL简介 (1)简单介绍 (2)Datasets & DataFrames (3)Spark SQL架构 (4)Spark ...

  3. python协程详解_对Python协程之异步同步的区别详解

    一下代码通过协程.多线程.多进程的方式,运行代码展示异步与同步的区别. import gevent import threading import multiprocessing # 这里展示同步和异 ...

  4. DeepLearning tutorial(1)Softmax回归原理简介+代码详解

    FROM: http://blog.csdn.net/u012162613/article/details/43157801 DeepLearning tutorial(1)Softmax回归原理简介 ...

  5. DeepLearning tutorial(3)MLP多层感知机原理简介+代码详解

    FROM:http://blog.csdn.net/u012162613/article/details/43221829 @author:wepon @blog:http://blog.csdn.n ...

  6. DeepLearning tutorial(4)CNN卷积神经网络原理简介+代码详解

    FROM: http://blog.csdn.net/u012162613/article/details/43225445 DeepLearning tutorial(4)CNN卷积神经网络原理简介 ...

  7. 空字符python_Python中的None与 NULL(即空字符)的区别详解

    1.首先要了解Python的对象的概念: Python中,万物皆对象,所有的操作都是针对对象的,那什么是对象,5是一个int对象,'oblong'是一个str对象,异常也是一个对象,抽象一点是,人,猫 ...

  8. java之yield(),sleep(),wait()区别详解-备忘笔记

    java之yield(),sleep(),wait()区别详解-备忘笔记 1.sleep() 使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁.也就是说如 ...

  9. 负载均衡原理与实践详解 第三篇 服务器负载均衡的基本概念-网络基础

    负载均衡原理与实践详解 第三篇 服务器负载均衡的基本概念-网络基础 系列文章: 负载均衡详解第一篇:负载均衡的需求 负载均衡详解第二篇:服务器负载均衡的基本概念-网络基础 负载均衡详解第三篇:服务器负 ...

最新文章

  1. Excel的数据分析—排位与百分比
  2. 在DbGridEh中显示主从表
  3. numpy.newaxis详解
  4. 星跃计划 | 新项目持续招募中!MSR Asia-MSR Redmond 联合科研计划邀你申请!
  5. Entity Framework Core Like 查询揭秘
  6. java堆 数据结构 堆_Java中的紧凑堆外结构/组合
  7. 事件处理-注册时间 // 事件处理-修饰符 // 事件处理-键盘事件的修饰符 // 事件处理-系统修饰符 // 事件处理-鼠标修饰符
  8. python实现范围框跟随_调整边界框的大小和位置,同时使其稍微居中
  9. jfreechart linux图片中文显示乱码解决方法
  10. 从源码入手,一文带你读懂Spring AOP面向切面编程
  11. atitit 软件框架类库设计的艺术.docx 目录 1. index 1 2. 第2章 设计api的动力之源 14 2 2.1. .1 分布式开发 14 2 2.2. 2.2 模块化应用程序 16
  12. 华为手机文件在内部存储路径_华为手机查找文件路径 华为文件夹在哪里
  13. android图形开发工具,Android开发实现的几何图形工具类GeometryUtil完整实例
  14. matlab igbt 关断,IGBT关断过程的分析
  15. 下了一个游戏说计算机丢失,冰封64位win10系统下启动游戏提示计算机丢失XINPUT1-3.dll怎么办...
  16. swagger的详细注解
  17. Http的会话跟踪和跨站攻击(xss)
  18. 阿里云短信接口方法使用
  19. Insyde uefi 隐藏设置_联想台式机10代cpu装win10及bios设置教程(新bios设置)
  20. win7系统什么时候停止服务器,Win7系统什么时候停止服务?Win7停止更新时间一览表图解...

热门文章

  1. AD域机器如何指定时钟服务器,active-directory – 如何让我的域控制器与正确的外部时间源同步?...
  2. 树莓派3 kali linux很卡,树莓派3装kali Linux 成功写入 但是点不亮 为什么?
  3. centos部署python个人博客项目
  4. 记一次对学校的渗透测试
  5. 《剑指offer》从上往下打印二叉树
  6. JS模块化工具requirejs教程
  7. 服务端大量CLOSE_WAIT问题的解决
  8. 让Elasticsearch飞起来!百亿级实时查询优化实战
  9. 算法太多挑花眼?教你如何选择正确的机器学习算法
  10. 公用技术——设计模式19——行为型模式——备忘录模式——待补充