可口的JAVA-并发控制之CountDownLatch
前言
本文带大家系统的学习一下CountDownLatch知识,从介绍、方法、场景、原理四个角度方面开展学习,话不多说,马上开始。
一:介绍
CountDownLatch是一种同步辅助工具,允许一个或多个线程等待*其他线程中正在执行的一组操作完成。
CountDownLatch 使用给定的 count 进行初始化。* {await} 方法阻塞,直到当前计数达到零,因为调用了 {countDown} 方法,之后*所有等待线程被释放,并且任何后续调用* {await } 立即返回。这是一种一次性现象,计数无法重置。如果您需要重置计数的版本,请考虑使用 CyclicBarrier。
二:方法
CountDownLatch类对外开放的方法有
- void await(); 启动线程等待
- void countDown(); 等待数目减一
- int getCount(); 获取当前等待线程数目
** 分享一段代码帮助大家理解**
以幼儿园呼叫孩子们做游戏为例子,老师要一个一个通知到孩子,孩子们签到,开始做游戏。 Flowerd.class
/*** @class 孩子*/ class Flowerd implements Runnable{public Flowerd(CountDownLatch latch_s){latch = latch_s;}private CountDownLatch latch;@Overridepublic void run() {try {//1.孩子们反应时间不同,用一个随机数Thread.sleep((int)(1000*Math.random()));System.out.println(Thread.currentThread().getName()+"::来了,还差【"+(latch.getCount()-1)+"】个人!");latch.countDown();} catch (InterruptedException e) {e.printStackTrace();}} } 复制代码
playGame.method
/*** @function 喊孩子们做游戏*/ public static void playGame() {System.out.println("开始呼叫孩子们");CountDownLatch downLatch = new CountDownLatch(3);System.out.println("呼叫小明。。。");new Thread(new Flowerd(downLatch),"小明").start();System.out.println("呼叫小滑。。。");new Thread(new Flowerd(downLatch),"小滑").start();System.out.println("呼叫小六。。。");new Thread(new Flowerd(downLatch),"小六").start();System.out.println("孩子们呼叫完毕");try {downLatch.await();System.out.println("孩子们都来了,开始做游戏吧。。。。");} catch (InterruptedException e) {e.printStackTrace();} } 复制代码
执行结果:
开始呼叫孩子们 呼叫小明。。。 呼叫小滑。。。 呼叫小六。。。 孩子们呼叫完毕 小滑::来了,还差【2】个人! 小六::来了,还差【1】个人! 小明::来了,还差【0】个人! 孩子们都来了,开始做游戏吧。。。。 复制代码
CountDownLatch初始化了三个孩子,主线程调用await阻塞,子线程调用countDown减一,当三个孩子全部报道后await方法自动释放。 另外,await方法支持设置定时器,超时自动释放。
boolean await(long timeout, TimeUnit unit) 复制代码
替换代码:
downLatch.await(300l, TimeUnit.MILLISECONDS); 复制代码
替换后执行结果:
开始呼叫孩子们 呼叫小明。。。 呼叫小滑。。。 呼叫小六。。。 孩子们呼叫完毕 小六::来了,还差【2】个人! 小滑::来了,还差【1】个人! 孩子们都来了,开始做游戏吧。。。。 小明::来了,还差【0】个人! 复制代码
三:场景
大家了解了CountDownLatchiben方法和特性后,可以进行合理推测:countdownlatch就是一把等待 锁,可以我等你也可以你等我,可以一个资源等多个资源,也可以多个资源等一个资源,延伸一下就是多个资源等多个资源。
场景一:一个资源等多个资源
某聚合接口的调用,需要整合底层多个服务调用结果,使用同步调用很显然不行(除非客户没有性能方面要求),异步调用后整合子服务结果返回。
public static void main(String[] args) {callAPI(); }/*** @function 模拟聚合接口调用*/ static void callAPI(){CountDownLatch latch = new CountDownLatch(3);//调用服务ASystem.out.println("进入api");System.out.println("调用A服务");FutureTask<Integer> ft1 = new FutureTask<>(new BaseService(latch));new Thread(ft1,"底层服务A").start();//调用服务BSystem.out.println("调用B服务");FutureTask<Integer> ft2 = new FutureTask<>(new BaseService(latch));new Thread(ft2,"底层服务B").start();//调用服务CSystem.out.println("调用C服务");FutureTask<Integer> ft3 = new FutureTask<>(new BaseService(latch));new Thread(ft3,"底层服务C").start();try {System.out.println("等待子服务调用结果。。。");latch.await();System.out.println("API结果为:【"+(ft1.get()+ft2.get()+ft3.get())+"】");} catch (InterruptedException e) {e.printStackTrace();}catch (ExecutionException e){e.printStackTrace();} } 复制代码
/*** 可调用底层服务*/ class BaseService implements Callable<Integer>{private CountDownLatch latch;public BaseService(CountDownLatch latch1){latch = latch1;}@Overridepublic Integer call() throws Exception {Thread.sleep((int)Math.random()*1000);Integer re = (int)(Math.random()*10000);latch.countDown();System.out.println(Thread.currentThread().getName()+"服务执行完毕,返回【"+re+"】");return re;} } 复制代码
执行结果:
进入api 调用A服务 调用B服务 调用C服务 等待子服务调用结果。。。 底层服务A服务执行完毕,返回【5643】 底层服务C服务执行完毕,返回【639】 底层服务B服务执行完毕,返回【3524】 API结果为:【9806】 复制代码
场景二:多个资源等一个资源
运动场上,选手准备完毕后,等待裁判发令枪响,然后同时起跑。 示例代码:
/*** 运动员*/ class RunMan implements Runnable{public RunMan(CountDownLatch latch_s){latch = latch_s;}private CountDownLatch latch;@Overridepublic void run() {System.out.println("运动员【"+Thread.currentThread().getName()+"】准备完毕,等待裁判发令枪响。。。");try {latch.await();Thread.sleep((int)Math.random()*1000);System.out.println("运动员【"+Thread.currentThread().getName()+"】到达终点");} catch (InterruptedException e) {e.printStackTrace();}} } 复制代码
public static void main(String[] args) {raceArea(); } /*** 赛场*/ static void raceArea(){System.out.println("开始准备");CountDownLatch downLatch = new CountDownLatch(1);new Thread(new RunMan(downLatch),"小明").start();new Thread(new RunMan(downLatch),"小滑").start();new Thread(new RunMan(downLatch),"小六").start();try {Thread.sleep(2000);System.out.println("裁判员开枪。。。。");downLatch.countDown();Thread.sleep(2000);System.out.println("比赛结束");} catch (InterruptedException e) {e.printStackTrace();}} 复制代码
执行结果:
开始准备 运动员【小滑】准备完毕,等待裁判发令枪响。。。 运动员【小六】准备完毕,等待裁判发令枪响。。。 运动员【小明】准备完毕,等待裁判发令枪响。。。 裁判员开枪。。。。 运动员【小滑】到达终点 运动员【小明】到达终点 运动员【小六】到达终点 比赛结束 复制代码
场景三:多个资源等多个资源
例如:汽车和电瓶车过马路,首先要保证是绿灯,其次要保证没有行人正在闯红灯横穿马路,我们可以设置两个线程(行人+绿灯)做等待条件,再设置两个线程(汽车和电瓶车)做要执行的条件,与上述两个功能都有类似之处,代码大家调整修改一下就OK了,搞不定的评论一下我再写。
四:部分原理
要点一:CountDownLatch内部使用了 AbstractQueuedSynchronizer(fifo)抽象同步队列的来保证同步。 源码01:
要点二:采用cas乐观锁控制CountDownLatch的递减,重写了tryReleaseShared()方法。源码02:
要点三:CountDownLatch是不可重复指定的,只能初始化一次,这点和CyclicBarrier有区别。
可口的JAVA-并发控制之CountDownLatch相关推荐
- java并发中CountDownLatch的使用
文章目录 主线程等待子线程全都结束之后再开始运行 等待所有线程都准备好再一起执行 停止CountdownLatch的await java并发中CountDownLatch的使用 在java并发中,控制 ...
- Java笔记-对CountDownLatch的理解(对比Qt中的QSemaphore)含实例
首先在CountDownLatch,这个东西基本上和信号量是一样的,这个CountDownLatch要设置一个初值,这个值一般是个正值,可以对这个CountDownLatch进行countDown() ...
- Java 并发编程CountDownLatch的应用与源码解析
应用场景 CountDownLatch是一个多线程控制工具.用来控制线程的等待. 设置需要countDown的数量,然后每一个线程执行完毕后调用countDown()方法,而在主线程中调用await( ...
- java并发初探CountDownLatch
java并发初探CountDownLatch CountDownLatch是同步工具类能够允许一个或者多个线程等待直到其他线程完成操作. 当前前程A调用CountDownLatch的await方法进入 ...
- Java多线程并发控制工具CountDownLatch,实现原理及案例
跟着作者的65节课彻底搞懂Java并发原理专栏,一步步彻底搞懂Java并发原理. 作者简介:笔名seaboat,擅长工程算法.人工智能算法.自然语言处理.架构.分布式.高并发.大数据和搜索引擎等方面的 ...
- Java并发——结合CountDownLatch源码、Semaphore源码及ReentrantLock源码来看AQS原理
前言: 如果说J.U.C包下的核心是什么?那我想答案只有一个就是AQS.那么AQS是什么呢?接下来让我们一起揭开AQS的神秘面纱 AQS是什么? AQS是AbstractQueuedSynchroni ...
- LeetCode 1195. Fizz Buzz Multithreaded--并发系列题目--Java 解法--AtomicInteger/CountDownLatch/CyclicBarrier
题目地址:Fizz Buzz Multithreaded - LeetCode Write a program that outputs the string representation of nu ...
- Java进阶:CountDownLatch倒计时
概述 CountDownLatch 倒计时器,通常用于堵塞某个线程,直到其他前置线程都执行完毕后,堵塞线程才继续执行. CountDownLatch 是基于CAS实现的,因此它是线程安全的.CAS相关 ...
- Java并发教程– CountDownLatch
Java中的某些并发实用程序自然会比其他并发实用程序受到更多关注,因为它们可以解决通用问题而不是更具体的问题. 我们大多数人经常遇到执行程序服务和并发集合之类的事情. 其他实用程序不太常见,因此有时它 ...
最新文章
- darknet: ./src/cuda.c:36: check_error: Assertion `0' failed.
- OGG 跳过事务(转)
- 关于java中多态的理解,涉及到内存空间
- Thinkphp 打印最近执行的一条SQL语句
- 进程、线程和协程之间的区别和联系
- Java中的四种引用方式的区别
- Hangfire源码解析-如何实现可扩展IOC的?
- 汇编语言重写除零中断
- linux配置ip地址 suse_SUSE Linux下设置IP的两种方法
- Educational Codeforces Round 81 (Rated for Div. 2) C. Obtain The String 序列自动机
- terminal登录mysql_转载-MySQL之终端(Terminal)管理MySQL
- 面试 AI 算法岗,你被要求复现顶会了嘛?
- 利用第三方库XML解析 (TBXML)转化成模型数据
- [BZOJ]1003 物流运输(ZJOI2006)
- 【C语言】 扫雷游戏(保姆级的实现过程)
- SPI 接口驱动电路设计
- linux桌面下雪,分享|Linux/Unix 桌面趣事:让桌面下雪
- 他是学计算机的这个句子中宾语是动词性的,《现代汉语语法修辞》 综合试卷有全部答案...
- word如何给数学公式编号
- Tomcat详细配置(全)
热门文章
- hog svm 视频 matlab,matlab版hog+svm图像二分类
- byte 类型比较_C++数据类型回顾
- 如何在VMware中安装Linux系统(带界面)~新手向
- VTLN(Vocal Tract Length Normalisation)
- 我的世界minecraft-Python3.9编程(3)-创建一根柱子
- linux内核杂记(6)-进程调度(1)
- linux内核杂记(5)-进程终结
- SDOI 2009 ED
- 聊一聊转行推荐的问题
- 【深度学习】一文深度解读模型评估方法