应用场景

CountDownLatch是一个多线程控制工具。用来控制线程的等待。

设置需要countDown的数量,然后每一个线程执行完毕后调用countDown()方法,而在主线程中调用await()方法等待,直到num个子线程执行了countDown()方法,则主线程开始继续执行

示例:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class CountDownLatchDemo {private static final int nums = 5;public static CountDownLatch countDownLatch = new CountDownLatch(nums);public static void main(String[] args) throws Throwable {ExecutorService es = Executors.newFixedThreadPool(5);int i = 0;while (i < 5) {es.submit(new CountDownLatchRunning(countDownLatch, i));i++;}countDownLatch.await();System.out.println("任务全部执行完毕!");es.shutdown();}
}class CountDownLatchRunning implements Runnable {private int i = 0;private CountDownLatch countDownLatch;public CountDownLatchRunning(CountDownLatch countDownLatch, int i) {this.i = i;this.countDownLatch = countDownLatch;}@Overridepublic void run() {try {Thread.sleep(1000); // 任务执行了1秒if (i == 3) {throw new RuntimeException();}System.out.println(Thread.currentThread().getName() + ": 任务执行完毕!");} catch (InterruptedException e) {e.printStackTrace();} finally {countDownLatch.countDown();}}
}

main主线程会一致阻塞等待10条CountDownLatchRunning线程全部执行完成,当countDown()被调用10次之后,mian主线程继续执行

源码解析

CountDownLatch主要是两个方法:await()、countDown(),还有一个构造方法 CountDownLatch(int count)

构造方法:CountDownLatch(int count)

    public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");// 实例化一个Sync对象this.sync = new Sync(count);}

通过构造方法去设置AQS state的初始值为count,Sync是在CountDownLatch类中实现的AQS实现类

阻塞方法:await()

在例子中也有提到,await()会阻塞主线程,直到所有线程都countDown()数量等于count,也就是state==0

    public void await() throws InterruptedException {// 共享式获取AQS的同步状态sync.acquireSharedInterruptibly(1);}

调用的是AQS的acquireSharedInterruptibly方法:

 public final void acquireSharedInterruptibly(int arg)throws InterruptedException {if (Thread.interrupted()) // 线程中断 说明闭锁对线程中断敏感throw new InterruptedException();if (tryAcquireShared(arg) < 0) // 闭锁未使用完成 线程进入同步队列自旋等待 doAcquireSharedInterruptibly(arg);}

其中tryAcquireShared依赖于Sync的实现:

    /** 获取共享锁 */protected int tryAcquireShared(int acquires) {// AQS的同步状态为0则闭锁结束 可以进行下一步操作return (getState() == 0) ? 1 : -1;}

也就是需要当getState() == 0的时候,才可以进行继续执行,否则线程进入同步队列自旋等待(AQS同步队列的自旋等待)

计数方法:countDown()

调用countDown()方法会将计数器减1,直到计数器为0,代码如下:

public void countDown() {sync.releaseShared(1);
}

同样调用的是AQS的releaseShared方法:

 public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) { // 减少闭锁的计数器,只有计数器为0的时候才会返回truedoReleaseShared(); // 唤醒被await方法阻塞的所有线程return true;}return false;}

这里的返回这对CountDownLatch没有用,其中tryReleaseShared方法依赖的是Sync的实现:

        /** 释放共享锁 */protected boolean tryReleaseShared(int releases) {// 死循环,如果CAS操作失败就会不断继续尝试for (;;) {int c = getState();if (c == 0) // 正常不会进入此逻辑return false;int nextc = c-1; // 将计数器-1if (compareAndSetState(c, nextc)) // 更新计数器// 如果操作成功,返回计数器是否为0,直接关系到是否执行doReleaseShared方法来唤醒后续线程return nextc == 0;}}

可以看到,只有当计数器等于0的时候才会返回true,才会唤醒后续线程(调用await()自旋等待的线程)

带有超时等待的方法:await(long timeout, TimeUnit unit)

和await()类似,只是加入了超时检测,在自旋等待的过程中会去检查是否超时,超时则结束

总结

CountDownLatch类使用AQS同步器来实现计数。

  1. 在await()的时候,调用await()的线程将进入自旋等待,自旋过程会调用AQS的Sync实现的tryAcquireShared方法,只有当闭锁计数器等于0的时候,线程才能够继续执行
  2. 在其它线程调用countDown的方法时候,会将闭锁计数器减1(state-1),直至计数器等于0的时候,会唤醒后续线程获取闭锁(doReleaseShared),自旋等待中的线程才可以继续执行

Java 并发编程CountDownLatch的应用与源码解析相关推荐

  1. Java 并发编程CyclicBarrier的应用与源码解析(基于ReentrantLock实现)

    什么是CyclicBarrier? CyclicBarrie和上一篇中讲到CountDownLatch很类似,它能阻塞一组线程直到某个事件的发生. 栅栏与闭锁的关键区别在于:所有必须同时到达栅栏位置才 ...

  2. Java 并发编程Semaphore的应用与源码解析

    What Semaphore标识信号量,允许指定数量的线程同时访问某个资源 How 通过以下两部实现信号量: acquire方法用于获得准入许可(如果没有获得许可,则进行等待,直到有线程释放许可而获得 ...

  3. Java并发编程与技术内幕:ConcurrentHashMap源码解析

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文主要讲了Java中ConcurrentHashMap 的源码 ConcurrentH ...

  4. Java并发编程笔记之Semaphore信号量源码分析

    JUC 中 Semaphore 的使用与原理分析,Semaphore 也是 Java 中的一个同步器,与 CountDownLatch 和 CycleBarrier 不同在于它内部的计数器是递增的,那 ...

  5. Java并发编程(十六):CyclicBarrier源码分析

    前言   CyclicBarrier可以建立一个屏障,这个屏障可以阻塞一个线程直到指定的所有线程都达到屏障.就像团队聚餐,等所有人都到齐了再一起动筷子.根据Cyclic就可以发现CyclicBarri ...

  6. 【Java并发编程】16、ReentrantReadWriteLock源码分析

    一.前言 在分析了锁框架的其他类之后,下面进入锁框架中最后一个类ReentrantReadWriteLock的分析,它表示可重入读写锁,ReentrantReadWriteLock中包含了两种锁,读锁 ...

  7. java并发编程基础-ReentrantLock及LinkedBlockingQueue源码分析

    ReentrantLock是一个较为常用的锁对象.在上次分析的uil开源项目中也多次被用到,下面谈谈其概念和基本使用. 概念 一个可重入的互斥锁定 Lock,它具有与使用 synchronized 相 ...

  8. c++ 线程池_JAVA并发编程:线程池ThreadPoolExecutor源码分析

    前面的文章已经详细分析了线程池的工作原理及其基本应用,接下来本文将从底层源码分析一下线程池的执行过程.在看源码的时候,首先带着以下两个问题去仔细阅读.一是线程池如何保证核心线程数不会被销毁,空闲线程数 ...

  9. 多线程与高并发(八):ThreadPoolExecutor源码解析, SingleThreadPool,CachedPool,FixedThreadPool,ForkJoinPoll 等

    线程池 今天我们来看看JDK给我们提供的默认的线程池的实现. ThreadPoolExecutor:我们通常所说的线程池.多个线程共享同一个任务队列. SingleThreadPool CachedP ...

最新文章

  1. IDEA 打可执行jar包(maven项目)
  2. 循环往list中add对象却总是add的是一个对象
  3. IOS15自定义UICollectionView的使用
  4. Spring Boot学习总结(27)—— Spring Boot中两个数据库迁移工具Liquibase和Flyway的比较
  5. CAP、BASE、ACID基本概念
  6. sqlserver 2005 快速插入数据
  7. 拆解日本松下的老古董收录机,感受50年前的电路设计,满满的历史感
  8. sublime好看的字体设置
  9. 刘宇凡:罗永浩的锤子情怀只能拿去喂狗
  10. 【原创】从文华财经下载数据并且分析每个期货品种的贝塔值-云金杞
  11. 科比历年全部比赛合集【百度网盘高清分享】
  12. Androd Camera Yuv Jepg bmp
  13. 手动删除oracle数据库
  14. java 吸血鬼数字_java编程思想之吸血鬼数字
  15. wke播放优酷提示客户端权限的问题
  16. 短租民宿多平台房态同步管理系统
  17. 同期收治患者住院天数_速看!二级公立中医医院绩效考核指标发布(附34个指标)...
  18. HDU 2550 百步穿杨
  19. java bufferedread_java中关于bufferedreader类中read方法
  20. nmos管(型号IRLR8726)中文资料汇总

热门文章

  1. php 单例模式 构函数,PHP设计模式——单例模式(Singleton Pattern)
  2. 测试开发系类之接口自动化测试
  3. PHP中||与or的区别
  4. .Net水晶报表的使用总结
  5. SQL 2000 中如何 纵表变横表
  6. Mysql 获取年级每个班前十学生的信息
  7. 如何给Xcode添加我们常用的插件呢?
  8. docker高级应用之智能添加与修改防火墙规则
  9. MySQL prepare语句的SQL语法
  10. (十五) 构建springmvc+mybatis+dubbo分布式平台-window安装dubbo管控台