CountDownLatch闭锁也是基于AQS实现的一种同步器,它表示了“所有线程都等待,直到锁打开才继续执行”的含义。它和Semaphore的语意不同, Semaphore的获取和释放操作都会修改状态,都可能让自己或者其他线程立刻拿到锁。而闭锁的获取操作只判断状态是否为0,不修改状态本身,闭锁的释放操作会修改状态,每次递减1,直到状态为0。

所以正常情况下,闭锁的获取操作只是等待,不会立刻让自己获得锁,直到释放操作把状态变为0。

闭锁可以用来实现很多场景,比如:

1. 某个服务依赖于其他服务的启动才能启动,就可以让这个服务在其他服务状态的闭锁上等待

2. 某个游戏,必须等所有就绪者都到达才能开始游戏

3. 启动一组相关的线程

4. 等待一组相关线程结束

来看看CountDownLatch的代码。它也提供了一个内部类Sync来继承AQS

1. CountDownLatch可以让多个线程同时进入临界区,所以也是共享模式的AQS

2. 获取操作只是判断状态是否为0,即是否可以结束等待,进入临界区

3. 释放操作是对状态递减1,所以叫CountDown,类似报数的意思

 
  1. private static final class Sync extends AbstractQueuedSynchronizer {

  2. private static final long serialVersionUID = 4982264981922014374L;

  3. Sync(int count) {

  4. setState(count);

  5. }

  6. int getCount() {

  7. return getState();

  8. }

  9. protected int tryAcquireShared(int acquires) {

  10. return (getState() == 0) ? 1 : -1;

  11. }

  12. protected boolean tryReleaseShared(int releases) {

  13. // Decrement count; signal when transition to zero

  14. for (;;) {

  15. int c = getState();

  16. if (c == 0)

  17. return false;

  18. int nextc = c-1;

  19. if (compareAndSetState(c, nextc))

  20. return nextc == 0;

  21. }

  22. }

  23. }

CountDownLatch维护了一个状态表示Count的总数,释放一次对这个总数减1直到为0,它的tryXXX方法传递的参数没有实际意义,只是为了适应接口。

如果获取失败,就进入AQS等待,直到等待结束后,以共享的方式在AQS队列中释放线程。

CountDownLatch常用的方法就两个: await()和countDown()

 
  1. public CountDownLatch(int count) {

  2. if (count < 0) throw new IllegalArgumentException("count < 0");

  3. this.sync = new Sync(count);

  4. }

  5. // 等待就相当于获取操作

  6. public void await() throws InterruptedException {

  7.         sync.acquireSharedInterruptibly(1);

  8.     }

  9. // 限时等待

  10. public boolean await(long timeout, TimeUnit unit)

  11.         throws InterruptedException {

  12.         return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));

  13.     }

  14. public void countDown() {

  15.         sync.releaseShared(1);

  16.     }

设计1个测试用例来测试CountDownLatch闭锁的功能

1.  创建1个二元闭锁startLatch,只有1和0两种状态,也就是说只执行一次countDown()就可以打开闭锁。这个startLatch用来阻塞线程,直到主线程说可以开始了

2.  创建1个状态为n的endLatch,线程执行完就调用一次countDown,主线程在endLatch阻塞,直到n个线程都执行了countDown()报数,主线程才打印结束

 
  1. package com.zc.lock;

  2. import java.util.concurrent.CountDownLatch;

  3. public class CountDownLatchUsecase {

  4. private int nThreads;

  5. private CountDownLatch startLatch;

  6. private CountDownLatch endLatch;

  7. public CountDownLatchUsecase(int n){

  8. this.nThreads = n;

  9. startLatch = new CountDownLatch(1);

  10. endLatch = new CountDownLatch(nThreads);

  11. }

  12. public void race() throws InterruptedException{

  13. System.out.println("Thread " + Thread.currentThread().getName() + " is waiting the resource");

  14. startLatch.await();

  15. System.out.println("Thread " + Thread.currentThread().getName() + " got the resource");

  16. endLatch.countDown();

  17. }

  18. public void start(){

  19. startLatch.countDown();

  20. }

  21. public void end() throws InterruptedException{

  22. endLatch.await();

  23. }

  24. public static void main(String[] args) throws Exception{

  25. final CountDownLatchUsecase usecase = new CountDownLatchUsecase(10);

  26. for(int i = 0; i < 10; i++){

  27. Thread t = new Thread(new Runnable(){

  28. @Override

  29. public void run() {

  30. try {

  31. usecase.race();

  32. } catch (InterruptedException e) {

  33. e.printStackTrace();

  34. }

  35. }

  36. }, String.valueOf(i));

  37. t.start();

  38. }

  39. Thread.sleep(3000);

  40. System.out.println("Now start!!!");

  41. usecase.start();

  42. usecase.end();

  43. System.out.println("All Thread finished");

  44. }

  45. }

测试结果: 所有线程都等待,直到主线程说开始。所有线程都执行了countDown()之后,主线程才说结束

 
  1. Thread 0 is waiting the resource

  2. Thread 2 is waiting the resource

  3. Thread 3 is waiting the resource

  4. Thread 1 is waiting the resource

  5. Thread 4 is waiting the resource

  6. Thread 5 is waiting the resource

  7. Thread 6 is waiting the resource

  8. Thread 7 is waiting the resource

  9. Thread 8 is waiting the resource

  10. Thread 9 is waiting the resource

  11. Now start!!!

  12. Thread 0 got the resource

  13. Thread 2 got the resource

  14. Thread 3 got the resource

  15. Thread 8 got the resource

  16. Thread 7 got the resource

  17. Thread 6 got the resource

  18. Thread 5 got the resource

  19. Thread 1 got the resource

  20. Thread 4 got the resource

  21. Thread 9 got the resource

  22. All Thread finished

聊聊高并发(二十六)解析java.util.concurrent各个组件(八) 理解CountDownLatch闭锁相关推荐

  1. 聊聊高并发(十七)解析java.util.concurrent各个组件(一) 了解sun.misc.Unsafe类

    了解了并发编程中锁的基本原理之后,接下来看看Java是如何利用这些原理来实现各种锁,原子变量,同步组件的.在开始分析java.util.concurrent的源代码直接,首先要了解的就是sun.mis ...

  2. 聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类

    这篇说说java.util.concurrent.atomic包里的类,总共12个,网上有很多文章解析这几个类,这里挑些重点说说. 这12个类可以分为三组: 1. 普通类型的原子变量 2. 数组类型的 ...

  3. 聊聊高并发(二十一)解析java.util.concurrent各个组件(三) 深入理解AQS(一)

    AQS是AbstractQueuedSynchronizer的缩写,AQS是Java并包里大部分同步器的基础构件,利用AQS可以很方便的创建锁和同步器.它封装了一个状态,提供了一系列的获取和释放操作, ...

  4. 聊聊高并发(二十七)解析java.util.concurrent各个组件(九) 理解ReentrantLock可重入锁

    这篇讲讲ReentrantLock可重入锁,JUC里提供的可重入锁是基于AQS实现的阻塞式可重入锁.这篇 聊聊高并发(十六)实现一个简单的可重入锁 模拟了可重入锁的实现.可重入锁的特点是: 1. 是互 ...

  5. 聊聊高并发(三十)解析java.util.concurrent各个组件(十二) 理解CyclicBarrier栅栏

    这篇讲讲CyclicBarrier栅栏,从它的名字可以看出,它是可循环使用的.它的功能和CountDownLatch类似,也是让一组线程等待,然后一起开始往下执行.但是两者还是有几个区别 1. 等待的 ...

  6. 聊聊高并发(二十三)解析java.util.concurrent各个组件(五) 深入理解AQS(三)

    这篇对AQS做一个总结. 上一篇帖了很多AQS的代码,可以看出AQS的实现思路很简单,就是提供了获取acquire和释放操作release,提供了 1. 可中断和不可中断的版本 2. 可定时和不可定时 ...

  7. 聊聊高并发(四十)解析java.util.concurrent各个组件(十六) ThreadPoolExecutor源码分析

    ThreadPoolExecutor是Executor执行框架最重要的一个实现类,提供了线程池管理和任务管理是两个最基本的能力.这篇通过分析ThreadPoolExecutor的源码来看看如何设计和实 ...

  8. 聊聊高并发(二十二)解析java.util.concurrent各个组件(四) 深入理解AQS(二)

    上一篇介绍了AQS的基本设计思路以及两个内部类Node和ConditionObject的实现 聊聊高并发(二十一)解析java.util.concurrent各个组件(三) 深入理解AQS(一) 这篇 ...

  9. 聊聊高并发(二十五)解析java.util.concurrent各个组件(七) 理解Semaphore

    前几篇分析了一下AQS的原理和实现,这篇拿Semaphore信号量做例子看看AQS实际是如何使用的. Semaphore表示了一种可以同时有多个线程进入临界区的同步器,它维护了一个状态表示可用的票据, ...

  10. 聊聊高并发(二十九)解析java.util.concurrent各个组件(十一) 再看看ReentrantReadWriteLock可重入读-写锁

    上一篇聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁 讲了可重入读写锁的基本情况和主要的方法,显示了如何 ...

最新文章

  1. dig linux下的使用
  2. CCF 202104-4 校门外的树 Python
  3. php什么设置前端代码,代码编辑器与PHPSTUDY的安装与配置过程(前端第一课)
  4. leetcode3. Longest Substring Without Repeating Characters
  5. python - 列表
  6. 北京网信金服PHP薪资_【企航金服工资|企航金服待遇怎么样】-看准网
  7. garmin 945_点评:Garmin Nuvi 350 GPS
  8. 李宏毅机器学习笔记【未完】
  9. analyze怎么优化oracle,Analyze table对Oracle性能的提升
  10. SK18752功放板
  11. css特殊符号代码以及用过使用方式。
  12. Bus error (core dumped) 我重启了下superviser 资源cpu占用高
  13. 数据结构(二)----线性表(List)链式存储结构(1)
  14. Android图片上传服务器(File格式)
  15. 最初制造计算机的目的,31最早研究图像处理技术的目的是为了改善人类分析判断图像信息的能力随着计算机与人工智能技术的发展以及生产制造方面...
  16. 开博尔android播放器,双核ANDROID网络播放器-开博尔C9-开箱 + 评测
  17. 如果你是淘宝的产品经理,你该如何设计淘宝?
  18. draw.io在线画图
  19. C语言N个数内的奇数和/偶数和
  20. windows xp主题不见了

热门文章

  1. mc显示服务器生命值,[1.7-1.8]CombatIndicator — 全息显示攻击伤害的数值 让我的世界服务器更有游戏感...
  2. cmos全局曝光算法_2019腾讯广告算法大赛方案分享(冠军)
  3. android dimensions.xml,android – Value等于match_parent或fill_parent在dimensions.xml?
  4. catia圆管焊接焊接_CATIA焊接教程.ppt
  5. jpa 去重_spring boot jpa 表关联查询分组 group by 去重
  6. 底部分页栏_2020年执业药师考试教材各科目增加页数!最多203页
  7. html中红色星号,谨慎使用CSS中的星号(*)通配符
  8. xcode开发php,xcode怎么编写c语言
  9. git 使用_Git使用总结
  10. oracle 半连接 效率,关于oracle中的半连接