前言-CountDownLatch是什么?

CountDownLatch是具有synchronized机制的一个工具,目的是让一个或者多个线程等待,直到其他线程的一系列操作完成。

CountDownLatch初始化的时候,需要提供一个整形数字,数字代表着线程需要调用countDown()方法的次数,当计数为0时,线程才会继续执行await()方法后的其他内容。

CountDownLatch(int count);

对象中的方法

  1. getCount
  2. 返回当前的计数count值,
  1. public void countDown()
  2. 调用此方法后,会减少计数count的值。
  3. 递减后如果为0,则会释放所有等待的线程
  1. public void await()
  2. throws InterruptedException
  3. 调用CountDownLatch对象的await方法后。
  4. 会让当前线程阻塞,直到计数count递减至0

如果当前线程数大于0,则当前线程在线程调度中将变得不可用,并处于休眠状态,直到发生以下两种情况之一:

1、调用countDown()方法,将计数count递减至0。

2、当前线程被其他线程打断。

  1. public boolean await(long timeout,
  2. TimeUnit unit)
  3. throws InterruptedException

同时await还提供一个带参数和返回值的方法。

如果计数count正常递减,返回0后,await方法会返回true并继续执行后续逻辑。

或是,尚未递减到0,而到达了指定的时间间隔后,方法返回false。

如果时间小于等于0,则此方法不执行等待。

实际案例

join阻塞等待线程完成

首先建立3个线程。

  1. public class Worker1 implements Runnable {
  2. @Override
  3. public void run() {
  4. System.out.println("-线程1启动");
  5. try {
  6. Thread.sleep(13_000);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. System.out.println("线程1完成--我休眠13秒\r\n");
  11. }
  12. }
  13. public class Worker2 implements Runnable {
  14. @Override
  15. public void run() {
  16. System.out.println("-线程2启动");
  17. try {
  18. Thread.sleep(3_000);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.out.println("线程2完成--我休眠3秒\r\n");
  23. }
  24. }
  25. public class Worker3 implements Runnable {
  26. @Override
  27. public void run() {
  28. System.out.println("-线程3启动");
  29. try {
  30. Thread.sleep(3_000);
  31. } catch (InterruptedException e) {
  32. e.printStackTrace();
  33. }
  34. try {
  35. Thread.sleep(3_000);
  36. } catch (InterruptedException e) {
  37. e.printStackTrace();
  38. }
  39. System.out.println("线程3完成--我休眠6秒\r\n");
  40. System.out.println();
  41. }
  42. }
  43. public class Main {
  44. public static void main(String[] args) throws InterruptedException {
  45. Worker1 worker1 = new Worker1();
  46. Worker2 worker2 = new Worker2();
  47. Worker3 worker3 = new Worker3();
  48. Thread thread1 = new Thread(worker1,"线程1");
  49. Thread thread2 = new Thread(worker2,"线程2");
  50. Thread thread3 = new Thread(worker3,"线程3");
  51. thread1.start();
  52. thread2.start();
  53. thread3.start();
  54. thread1.join();
  55. thread2.join();
  56. thread3.join();
  57. System.out.println("主线程结束....");
  58. }
  59. }

打印结果如下:

  1. -线程3启动
  2. -线程2启动
  3. -线程1启动
  4. 线程2完成--我休眠3
  5. 线程3完成--我休眠6
  6. 线程1完成--我休眠13
  7. 主线程结束....
  8. Process finished with exit code 0

可以看出三个线程是并行执行的。启动顺序,并不和执行完毕的顺序一致,但可以明确的是,主线程为一直阻塞,直到三个线程执行完毕。

CountDownLatch用法

阿里巴巴的数据库连接池Druid中也用了countDownLatch来保证初始化。

  1. // 开启创建连接的线程,如果线程池createScheduler为null,
  2. //则开启单个创建连接的线程
  3. createAndStartCreatorThread();
  4. // 开启销毁过期连接的线程
  5. createAndStartDestroyThread();

自己编写一个例子:

这里模拟一种情况:

主线程 依赖 线程A初始化三个数据,才能继续加载后续逻辑。

  1. public class CountDownArticle {
  2. /**
  3. * 模拟 主线程 依赖 线程A初始化一个数据,才能继续加载后续逻辑
  4. */
  5. public static void main(String[] args) throws InterruptedException {
  6. AtomicReference<String> key = new AtomicReference<>("");
  7. CountDownLatch countDownLatch = new CountDownLatch(3);
  8. Thread t = new Thread(() -> {
  9. try {
  10. //休眠5秒,模拟数据的初始化
  11. TimeUnit.SECONDS.sleep(5);
  12. key.set("核心秘钥123456");
  13. System.out.println("数据1初始化完毕");
  14. //释放---此处可以在任何位置调用,很灵活
  15. countDownLatch.countDown();
  16. System.out.println("数据2初始化完毕");
  17. countDownLatch.countDown();
  18. System.out.println("数据3初始化完毕");
  19. countDownLatch.countDown();
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. });
  24. t.start();
  25. //等待数据初始化,阻塞
  26. countDownLatch.await();
  27. System.out.println("key:" + key.get());
  28. }
  29. }

打印内容如下:

  1. 数据1初始化完毕
  2. 数据2初始化完毕
  3. 数据3初始化完毕
  4. key:核心秘钥123456

CountDownLatch和Join用法的区别?

在使用join()中,多个线程只有在执行完毕之后欧才能被解除阻塞,而在CountDownLatch中,线程可以在任何时候任何位置调用countdown方法减少计数,通过这种方式,我们可以更好地控制线程的解除阻塞,而不是仅仅依赖于连接线程的完成。

join()方法的执行逻辑如下图所示:

原理

从源码可以看出,CountDownLatch是依赖于AbstractQueuedSynchronizer来实现这一系列逻辑的。

队列同步器AbstractQueuedSynchronizer

是一个用来构建锁和同步器的框架,它在内部定义了一个被标识为volatile的名为state的变量,用来表示同步状态。

多个线程之间可以通过AQS来独占式或共享式的抢占资源。

并且它通过内置的FIFO队列来完成线程的排队工作。

CountDownLatch中的Sync会优先尝试修改state的值,来获取同步状态。例如,如果某个线程成功的将state的值从0修改为1,表示成功的获取了同步状态。 这个修改的过程是通过CAS完成的,所以可以保证线程安全。

反之,如果修改state失败,则会将当前线程加入到AQS的队列中,并阻塞线程。

总结

CountDownLatch(int N) 中的计数器,可以让我们支持最多等待N个线程的操作完成,或是一个线程操作N次。

如果仅仅只需要等待线程的执行完毕,那么join可能就能满足。但是如果需要灵活的控制线程,使用CountDownLatch。

注意事项

countDownLatch.countDown();

这一句话尽量写在finally中,或是保证此行代码前的逻辑正常运行,因为在一些情况下,出现异常会导致无法减一,然后出现死锁。

CountDownLatch 是一次性使用的,当计数值在构造函数中初始化后,就不能再对其设置任何值,当 CountDownLatch 使用完毕,也不能再次被使用。

写在最后

为了方便大家学习讨论,我创建了一个java疑难攻坚互助大家庭,和其他传统的学习交流不同。本群主要致力于解决项目中的疑难问题,在遇到项目难以解决的

问题时,都可以在这个大家庭里寻求帮助。

公众号回复【问题的答案】进入:java中Integer包装类的基本数据类型是?

如果你也经历过遇到项目难题,无从下手,

他人有可能可以给你提供一些思路和看法,一百个人就有一百种思路,

同样,如果你也乐于帮助别人,那解决别人遇到的问题,也同样对你是一种锻炼。

欢迎来公众号【侠梦的开发笔记】,回复干货,领取精选学习视频一份

多线程之CountDownLatch的用法及原理笔记的更多相关文章

  1. Java并发编程之CountDownLatch的用法

    一.含义 CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能.CountDownLatch是一个同步的辅助类,它可以允许一个或多个线程等待, ...

  2. 多线程之CountDownLatch、CyclicBarrier和Semaphore

    Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch ...

  3. JAVA多线程之CountDownLatch与join的区别

    首先,我们来看一个应用场景1: 假设一条流水线上有三个工作者:worker0,worker1,worker2.有一个任务的完成需要他们三者协作完成,worker2可以开始这个任务的前提是worker0 ...

  4. Java多线程之CountDownLatch学习

    给出官网上的例子:http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html Java中conc ...

  5. JAVA多线程之CountDownLatch

    前序: 上周测试给开发的同事所开发的模块提出了一个bug,并且还是偶现. 经过仔细查看代码,发现是在业务中启用了多线程,2个线程同时跑,但是新启动的2个线程必须保证一个完成之后另一个再继续运行,才能消 ...

  6. 多线程之CountDownLatch和CyclicBarriar使用

    CountDownLatch和CyclicBarriar是java.util.concurrent包下面提供的多线程同步工具,两者有点相似,相当于计数器,但是用处还是有区别的. CountDownLa ...

  7. Java多线程之CountDownLatch和CyclicBarrier同步屏障的使用

      转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6558349.html  一:CountDownLatch CountDownLatch是一个执行 完成任务 ...

  8. 多线程之CountDownLatch

    下面请看一个应用场景:有1个driver和5个worker,需要满足以下两点要求: 当driver完成了全部的工作之后才允许worker们开始工作: 当所有的worker都完成了自己的工作之后,dri ...

  9. iOS多线程之8.NSOPeration的其他用法

      本文主要对NSOPeration的一些重点属性和方法做出介绍,以便大家可以更好的使用NSOPeration. 1.添加依赖 - (void)addDependency:(NSOperation * ...

随机推荐

  1. javaweb-选课系统

    选课系统中用到了4个表,分别是classs.yonghu.teacher.student.在用户中存放管理员的信息name和password以及id,在另三个表中存放对应的数据如图: calss: t ...

  2. 011 RGW的SwiftAPi支持

    一. Swift简介 openstack swift是openstack开源云计算项目开源的对象存储,提供了强大的扩展性.冗余和持久性 1.1 swift特性 极高的数据持久性 完全对称的系统架构 无 ...

  3. $Poj1737\ Connected\ Graph$ 计数类$DP$

    AcWing Description 求$N$个节点的无向连通图有多少个,节点有标号,编号为$1~N$. $1<=N<=50$ Sol 在计数类$DP$中,通常要把一个问题划分成若干个子问 ...

  4. 使用ASP.NET Core 3.x 构建 RESTful API - 4.2 过滤和搜索

    向Web API传递参数 数据可以通过多种方式来传给API. Binding Source Attributes 会告诉 Model 的绑定引擎从哪里找到绑定源. 共有以下六种 Binding Sou ...

  5. iOS-UITableView HeaderView随Cell一起移动

    我们在使用TableView的时候,有时会设置HeaderView,当我们滑动的时候,HeaderView不会随Cell滑出屏幕,而是会固定到导航栏下面.今天我们要实现HeaderView随滑动一起滑 ...

  6. 原生javascript 基础动画原理

    一.实现原理: 1.开定时器前先清除定时器 2.设置定时器 3.当前元素的位置 + 每一步的长度 4.当元素当前位置超过目标点时,把当前位置==目标点 5.设置元素位置,开始运动 6.判断当前位置如果 ...

  7. (1)解锁 MongoDB replica set核心姿势

    副本集Replica Set是一个术语,定义具有多节点的数据库集群,这些节点具有主从复制(master-slave replication) 且节点之间实现了自动故障转移. 这样的结构通常需要具有奇数 ...

  8. 1z0-062 题库解析5

    题目: Which three statements are true about Flashback Database? A. Flashback logs are written sequenti ...

  9. 《【面试突击】— Redis篇》-- Redis哨兵原理及持久化机制

    能坚持别人不能坚持的,才能拥有别人未曾拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! <[面试突击]— Redis篇>-- Redis哨兵原理及持久化机制 在这个系列里, ...

  10. 通过HttpClient的方式去Curd数据⭐⭐⭐⭐

    在网上看博客的时候,看到这系列的文章,别特帮,强烈推荐 里面有一章节是通过HttpClient的方法去更新数据的,新颖,记录下. ⭐⭐⭐1:创建一个Model数据模型 这个类创建一个数据对象,Http ...