CountDownLatch的作用类似于Thread.join()方法,但比join()更加灵活。它可以等待多个线程(取决于实例化时声明的数量)都达到预期状态或者完成工作以后,通知其他正在等待的线程继续执行。简单的说,Thread.join()是等待具体的一个线程执行完毕,CountDownLatch等待多个线程。

  如果需要统计4个文件中的内容行数,可以用4个线程分别执行,然后用一个线程等待统计结果,最后执行数据汇总。这样场景就适合使用CountDownLatch。

  本篇从CountDownLatch的源码分析它的原理机制。再给出一个简单的使用案例。

  

  首先认识一下CountDownLatch中的内部类:

  1. private static final class Sync extends AbstractQueuedSynchronizer {
  2. private static final long serialVersionUID = 4982264981922014374L;
  3.  
  4. Sync(int count) {
  5. setState(count); // 更新AQS中的state
  6. }
  7.  
  8. int getCount() {
  9. return getState();
  10. }
  11.  
  12. protected int tryAcquireShared(int acquires) {
  13. return (getState() == ) ? : -;
  14. }
  15.  
  16. protected boolean tryReleaseShared(int releases) {
  17. // Decrement count; signal when transition to zero
  18. for (;;) {
  19. int c = getState();
  20. if (c == )
  21. return false;
  22. int nextc = c-;
  23. if (compareAndSetState(c, nextc))
  24. return nextc == ;
  25. }
  26. }
  27. }

  其实CountDownLatch的机制和ReentrantLock有点像,都是利用AQS(AbstractQueuedSynchronizer)来实现的。CountDownLatch的内部类Sync继承AQS,重写了tryAcquireShared()方法和tryReleaseShared()方法。这里的重点是CountDownLatch的构造函数需要传入一个int值count,就是等待的线程数。这个count被Sync用来直接更新为AQS中的state。

  

1、await()等待方法

  1. //CountDownLatch
    public void await() throws InterruptedException {
  2. sync.acquireSharedInterruptibly();
  3. }
  4. //AQS
  5. public final void acquireSharedInterruptibly(int arg)
  6. throws InterruptedException {
  7. if (Thread.interrupted())
  8. throw new InterruptedException();
  9. if (tryAcquireShared(arg) < ) // 1
  10. doAcquireSharedInterruptibly(arg); // 2  
  11. }
  12. //Sync
  13. protected int tryAcquireShared(int acquires) {
  14. return (getState() == ) ? : -;
  15. }
  16. //AQS
  17. private void doAcquireSharedInterruptibly(int arg)
  18. throws InterruptedException {
  19. final Node node = addWaiter(Node.SHARED);
  20. boolean failed = true;
  21. try {
  22. for (;;) {
  23. final Node p = node.predecessor();
  24. if (p == head) {
  25. int r = tryAcquireShared(arg);
  26. if (r >= ) {
  27. setHeadAndPropagate(node, r);
  28. p.next = null; // help GC
  29. failed = false;
  30. return;
  31. }
  32. }
  33. if (shouldParkAfterFailedAcquire(p, node) &&
  34. parkAndCheckInterrupt())
  35. throw new InterruptedException();
  36. }
  37. } finally {
  38. if (failed)
  39. cancelAcquire(node);
  40. }
  41. }
  1. 调用AQS中的tryAcquireShared()方法时,Sync重写了tryAcquireShared()方法,获取state,判断state是否为0。
  2. 如果不为0,调用doAcquireSharedInterruptibly()方法,将线程加入队列,挂起线程。

2、countDown()

  1. public void countDown() {
  2. sync.releaseShared();
  3. }
    //AQS
  4. public final boolean releaseShared(int arg) {
  5. if (tryReleaseShared(arg)) {
  6. doReleaseShared();
  7. return true;
  8. }
  9. return false;
  10. }
    //Sync
  11. protected boolean tryReleaseShared(int releases) {
  12. // Decrement count; signal when transition to zero
  13. for (;;) {
  14. int c = getState();
  15. if (c == )
  16. return false;
  17. int nextc = c-;
  18. if (compareAndSetState(c, nextc))
  19. return nextc == ;
  20. }
  21. }

  重点也是在于Sync重写的tryReleaseShared()方法。利用CAS算法将state减1。如果state减到0,说明所有工作线程都执行完毕,那么就唤醒等待队列中的线程。

使用示例:

  1. public class CountDownLatchTest {
  2. private static CountDownLatch countDownLatch = new CountDownLatch();
  3. private static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(, ,
  4. 0L, TimeUnit.MILLISECONDS,
  5. new LinkedBlockingQueue<Runnable>(10));
  6.  
  7. public static void main(String[] args) {
  8. //等待线程
  9. for (int i = ; i < ; i++) {
  10. String threadName = "等待线程 " + i;
  11. threadPool.execute(new Runnable() {
  12.  
  13. @Override
  14. public void run() {
  15. try {
  16. System.out.println(threadName + " 正在等待...");
  17. //等待
  18. countDownLatch.await();
  19. System.out.println(threadName + " 结束等待...");
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. });
  25. }
  26. //工作线程
  27. for (int i = ; i < ; i++) {
  28. String threadName = "工作线程 " + i;
  29. threadPool.execute(new Runnable() {
  30.  
  31. @Override
  32. public void run() {
  33. try {
  34. System.out.println(threadName + " 进入...");
  35. //沉睡1秒
  36. TimeUnit.MILLISECONDS.sleep();
  37. System.out.println(threadName + " 完成...");
  38. //通知
  39. countDownLatch.countDown();
  40. } catch (InterruptedException e) {
  41. e.printStackTrace();
  42. }
  43. }
  44. });
  45. }
  46.  
  47. threadPool.shutdown();
  48. }
  49. }

  执行结果为:

  1. 等待线程 1 正在等待...
  2. 等待线程 0 正在等待...
  3. 工作线程 2 进入...
  4. 工作线程 3 进入...
  5. 工作线程 4 进入...
  6. 工作线程 3 完成...
  7. 工作线程 2 完成...
  8. 工作线程 4 完成...
  9. 等待线程 0 结束等待...
  10. 等待线程 1 结束等待...

  从结果也能看到,等待线程先执行,调用countDownLatch.await()方法开始等待。每个工作线程工作完成以后,都调用countDownLatch.countDown()方法,告知自己的任务完成。countDownLatch初始参数为3,所以3个工作线程都告知自己结束以后,等待线程才开始工作。

并发工具CountDownLatch源码分析的更多相关文章

  1. 并发工具CyclicBarrier源码分析及应用

      本文首发于微信公众号[猿灯塔],转载引用请说明出处 今天呢!灯塔君跟大家讲: 并发工具CyclicBarrier源码分析及应用 一.CyclicBarrier简介 1.简介 CyclicBarri ...

  2. Java - "JUC" CountDownLatch源码分析

    Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例 CountDownLatch简介 CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前 ...

  3. CountDownLatch 源码分析

    CountDownLatch 源码分析: 1:CountDownLatch数据结构 成员变量 Sync类型对象 private final Sync sync; Sync是继承AQS的一个类,Coun ...

  4. [软件测试]网站压测工具Webbench源码分析

    一.我与webbench二三事 Webbench是一个在linux下使用的非常简单的网站压测工具.它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能.Webbench ...

  5. 网站(Web)压测工具Webbench源码分析

    一.我与webbench二三事 Webbench是一个在linux下使用的非常简单的网站压测工具.它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能.Webbench ...

  6. Java并发——结合CountDownLatch源码、Semaphore源码及ReentrantLock源码来看AQS原理

    前言: 如果说J.U.C包下的核心是什么?那我想答案只有一个就是AQS.那么AQS是什么呢?接下来让我们一起揭开AQS的神秘面纱 AQS是什么? AQS是AbstractQueuedSynchroni ...

  7. JUC之CountDownLatch源码分析

    CountDownLatch是AbstractQueuedSynchronizer中共享锁模式的一个的实现,是一个同步工具类,用来协调多个线程之间的同步.CountDownLatch能够使一个或多个线 ...

  8. bootstrap_栅格系统_响应式工具_源码分析

    -----------------------------------------------------------------------------margin 为负 ​使盒子重叠 ​等高 等高 ...

  9. concurrent(五)同步辅助器CountDownLatch & 源码分析

    参考文档: https://blog.csdn.net/zxdfc/article/details/52752803 简介 CountDownLatch是一个同步辅助类.允许一个或多个线程等待其他线程 ...

随机推荐

  1. [luogu]P2279 [HNOI2003]消防局的设立[贪心]

    [luogu]P2279 [HNOI2003]消防局的设立 题目描述 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两 ...

  2. luogu P1028 数的计算 x

    P1028 数的计算 题目描述 我们要求找出具有下列性质数的个数(包含输入的自然数n): 先输入一个自然数n(n<=1000),然后对此自然数按照如下方法进行处理: 1.不作任何处理; 2.在它 ...

  3. apache Internal Server Error 解决方法

    https://blog.csdn.net/qq_33684377/article/details/78536548 https://blog.csdn.net/LJFPHP/article/deta ...

  4. [BZOJ3262]:陌上花开(CDQ分治)

    题目传送门 题目描述 有$n$朵花,每朵花有三个属性:花形$(s)$.颜色$(c)$.气味$(m)$,用三个整数表示.现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花$A$比 ...

  5. 浅谈IPv4至IPv6演进的实施路径

    作者:个推运维平台网络工程师 宗堂   1 业务背景 在互联网呈现爆炸式发展的今天, IPv4网络地址数量匮乏等问题将会影响到我国的互联网发展与应用,制约物联网.5G等新业务开展.今年4月国家工信部发 ...

  6. 大数据笔记(十六)——Hive的客户端及自定义函数

    一.Hive的Java客户端 JDBC工具类:JDBCUtils.java package demo.jdbc; import java.sql.DriverManager; import java. ...

  7. Ubuntu启动 卡在checking battery state 解决方案

    Ubuntu启动,卡在checking battery statALT + F1或者CTRL+ALT+F6切换到命令行[CTRL+ALT+F7返回界面]执行 sudo gdm start后就可以正常登 ...

  8. 豆瓣镜像安装python库

  9. 图书-软件架构:《Design Patterns: Elements of Reusable Object-Oriented Software》(即后述《设计模式》一书)

    ylbtech-图书-软件架构:<Design Patterns: Elements of Reusable Object-Oriented Software>(即后述<设计模式&g ...

  10. 分布式ID生成 - 雪花算法

    雪花算法是一种生成分布式全局唯一ID的经典算法,关于雪花算法的解读网上多如牛毛,大多抄来抄去,这里请参考耕耘的小象大神的博客ID生成器,Twitter的雪花算法(Java) 网上的教程一般存在两个问题 ...