死磕并发之CountDownLatch解析
CountDownLatch解析
CountDownLatch是什么
CountDownLatch是基于AQS的阻塞工具,阻塞一个或者多个线程,直到所有的线程都执行完成。
CountDownLatch解决了什么问题
当一个任务运算量比较大的时候,需要拆分为各种子任务,必须要所有子任务完成后才能汇总为总任务。
使用并发模拟的时候可以使用CountDownLatch.也可以设置超时等待时间,
CountDownLatch 用法
1.阻塞所有线程执行完成后再执行
@Slf4j
public class CountDownLatchExample {
//线程数量
private static final int THREAD_NUM = 10;
// CountdownLatch阻塞模拟
public static void main(String[] args) throws InterruptedException {
// 创建线程池 用于执行线程
ExecutorService executorService = Executors.newCachedThreadPool();
//创建countDownLatch
final CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
long startTime = System.currentTimeMillis();
//循环创建线程
for (int i = 0; i < THREAD_NUM; i++) {
final int a = i;
executorService.execute(() -> {
try {
test(a);
} catch (Exception e) {
log.error("Exception", e);
} finally {
countDownLatch.countDown();
}
});
}
countDownLatch.await();
long endTime = System.currentTimeMillis();
log.info("执行完毕,{}-{}",startTime,endTime);
executorService.shutdown();
}
private static void test(int num) throws InterruptedException {
Thread.sleep(100);
log.info("{}-{}", num,System.currentTimeMillis());
Thread.sleep(100);
}
}
结果
10:56:02.544 [pool-1-thread-5] INFO AQSExample.CountDownLatchExampleTimeOutTest - 4-1559271362542
10:56:02.543 [pool-1-thread-2] INFO AQSExample.CountDownLatchExampleTimeOutTest - 1-1559271362541
10:56:02.548 [pool-1-thread-10] INFO AQSExample.CountDownLatchExampleTimeOutTest - 9-1559271362548
10:56:02.544 [pool-1-thread-7] INFO AQSExample.CountDownLatchExampleTimeOutTest - 6-1559271362543
10:56:02.543 [pool-1-thread-4] INFO AQSExample.CountDownLatchExampleTimeOutTest - 3-1559271362542
10:56:02.544 [pool-1-thread-3] INFO AQSExample.CountDownLatchExampleTimeOutTest - 2-1559271362541
10:56:02.544 [pool-1-thread-8] INFO AQSExample.CountDownLatchExampleTimeOutTest - 7-1559271362543
10:56:02.544 [pool-1-thread-6] INFO AQSExample.CountDownLatchExampleTimeOutTest - 5-1559271362543
10:56:02.543 [pool-1-thread-1] INFO AQSExample.CountDownLatchExampleTimeOutTest - 0-1559271362541
10:56:02.548 [pool-1-thread-9] INFO AQSExample.CountDownLatchExampleTimeOutTest - 8-1559271362548
10:56:02.548 [main] INFO AQSExample.CountDownLatchExampleTimeOutTest - 执行完毕,1559271362441-1559271362548
上述结果可以看到,所有的线程执行完毕后主线程才打印出“执行完毕”。

2.按照超时时间阻塞所有线程执行,到时间后直接释放。
如果我们设置超时时间之后
@Slf4j
public class CountDownLatchExampleTimeOutTest {
//线程数量
private static final int THREAD_NUM = 10;
// CountdownLatch阻塞模拟
public static void main(String[] args) throws InterruptedException {
// 创建线程池 用于执行线程
ExecutorService executorService = Executors.newCachedThreadPool();
//创建countDownLatch
final CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
//循环创建线程
long startTime = System.currentTimeMillis();
for (int i = 0; i < THREAD_NUM; i++) {
final int a = i;
executorService.execute(() -> {
try {
test(a);
} catch (Exception e) {
log.error("Exception", e);
} finally {
countDownLatch.countDown();
}
});
}
countDownLatch.await(10,TimeUnit.MILLISECONDS);
long endTime = System.currentTimeMillis();
log.info("执行完毕,{}-{}",startTime,endTime);
executorService.shutdown();
}
private static void test(int num) throws InterruptedException {
Thread.sleep(50);
log.info("{}-{}", num,System.currentTimeMillis());
}
}
由于每个线程延迟50毫秒之后再执行,count已经超时了所以优先打印出了执行完毕的结果。然后在继续执行线程中的内容。
结果
11:14:55.509 [main] INFO AQSExample.CountDownLatchExampleTimeOutTest - 执行完毕,1559272495373-1559272495506
11:14:55.542 [pool-1-thread-1] INFO AQSExample.CountDownLatchExampleTimeOutTest - 0-1559272495542
11:14:55.542 [pool-1-thread-2] INFO AQSExample.CountDownLatchExampleTimeOutTest - 1-1559272495542
11:14:55.543 [pool-1-thread-3] INFO AQSExample.CountDownLatchExampleTimeOutTest - 2-1559272495543
11:14:55.543 [pool-1-thread-4] INFO AQSExample.CountDownLatchExampleTimeOutTest - 3-1559272495543
11:14:55.543 [pool-1-thread-5] INFO AQSExample.CountDownLatchExampleTimeOutTest - 4-1559272495543
11:14:55.544 [pool-1-thread-6] INFO AQSExample.CountDownLatchExampleTimeOutTest - 5-1559272495544
11:14:55.544 [pool-1-thread-7] INFO AQSExample.CountDownLatchExampleTimeOutTest - 6-1559272495544
11:14:55.545 [pool-1-thread-9] INFO AQSExample.CountDownLatchExampleTimeOutTest - 8-1559272495545
11:14:55.545 [pool-1-thread-8] INFO AQSExample.CountDownLatchExampleTimeOutTest - 7-1559272495545
11:14:55.545 [pool-1-thread-10] INFO AQSExample.CountDownLatchExampleTimeOutTest - 9-1559272495545
CountDownLatch源码解析
CountDownLatch源码中的方法和属性并不多,下面我们来一一解析。
1.AQS框架以及构造方法
//当前对象中私有阻塞工具
private final Sync sync;
// 模板方法模式重写AQS工具
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
// 共享阻塞AQS
Sync(int count) {
setState(count);
}
// 获取当前还剩多少资源可以使用
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
//构造方法创建一个锁对象
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
2.countDown()方法解析
该方法用于线程执行完毕后减计统计数量,
// 该方法时释放一个共享锁。当所有锁都被释放完成后主线程就能继续执行了。
public void countDown() {
sync.releaseShared(1);
}
3.await()方法解析
//拦截主线程的方法。主线程在这里等待条件达成后继续执行。
public void await() throws InterruptedException {
//在这里阻塞线程的执行
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//这里判断是否还有可以共享的资源
// 如果有则返回-1 否则返回 1,重写AQS的方法参见(1.AQS框架以及构造方法)
if (tryAcquireShared(arg) < 0)
// 有资源则运行阻塞自旋等待所有线程执行完毕
doAcquireSharedInterruptibly(arg);
// 无资源可用就让线程继续执行
}
// 带延迟的减少数据拦截方法
// 返回的结果是没有跑完全部线程就继续执行下一步了。
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
//线程如果被中断则抛出异常
if (Thread.interrupted())
throw new InterruptedException();
// 表示如果线程被执行完了直接返回成功,如果没有执行完则看等待时间来决定是否要继续执行。
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
CountDownLatch 总结
CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。 在分散计算统一合成结果,按某个流程加载资源的方面有着非诚好用的效果。下一篇我们讲解像蓄水池一样功能的Semphore。
死磕并发之CountDownLatch解析的更多相关文章
- 死磕 java同步系列之CyclicBarrier源码解析——有图有真相
问题 (1)CyclicBarrier是什么? (2)CyclicBarrier具有什么特性? (3)CyclicBarrier与CountDownLatch的对比? 简介 CyclicBarrier ...
- 死磕 java同步系列之Phaser源码解析
问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这 ...
- 死磕 java同步系列之StampedLock源码解析
问题 (1)StampedLock是什么? (2)StampedLock具有什么特性? (3)StampedLock是否支持可重入? (4)StampedLock与ReentrantReadWrite ...
- 【死磕 Spring】----- IOC 之解析 bean 标签:开启解析进程
原文出自:http://cmsblogs.com import 标签解析完毕了,再看 Spring 中最复杂也是最重要的标签 bean 标签的解析过程. 在方法 parseDefaultElement ...
- 【死磕 Spring】—– IOC 之解析Bean:解析 import 标签
原文出自:http://cmsblogs.com 在博客[死磕Spring]----- IOC 之 注册 BeanDefinition中分析到,Spring 中有两种解析 Bean 的方式.如果根节点 ...
- 死磕 java同步系列之volatile解析
问题 (1)volatile是如何保证可见性的? (2)volatile是如何禁止重排序的? (3)volatile的实现原理? (4)volatile的缺陷? 简介 volatile可以说是Java ...
- 死磕 java线程系列之线程池深入解析——普通任务执行流程
(手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本. 注:线程池源码部分如无特殊说明均指ThreadPoolExecutor类. 简介 前面我们一起学习了Java中 ...
- 死磕 java线程系列之线程池深入解析——定时任务执行流程
(手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本. 注:本文基于ScheduledThreadPoolExecutor定时线程池类. 简介 前面我们一起学习了普通 ...
- 死磕 java同步系列之Semaphore源码解析
问题 (1)Semaphore是什么? (2)Semaphore具有哪些特性? (3)Semaphore通常使用在什么场景中? (4)Semaphore的许可次数是否可以动态增减? (5)Semaph ...
随机推荐
- .netcore 图片处理
.netcore 图片处理 /// <summary> /// 根据文件类型和文件名返回新路径 /// </summary> /// <param name=" ...
- Java多线程和并发(十一),CAS(Compare and Swap)
目录 1.CAS简介 2.CAS多数情况下对开发者来说是透明的 3.CAS缺点 十一.CAS(Compare and Swap) Synchronized直观意义上是一种悲观锁 cas则是乐观锁的一种 ...
- Ueditor 从word中复制内容带多张图片
粘贴文本 注意,以下配置暂时对 IE 无效.IE 暂时使用系统自带的粘贴功能,没有样式过滤! 关闭粘贴样式的过滤 当从其他网页复制文本内容粘贴到编辑器中,编辑器会默认过滤掉复制文本中自带的样式,目的是 ...
- 2018 计蒜之道 初赛 第五场 A 贝壳找房搬家
贝壳找房换了一个全新的办公室,每位员工的物品都已经通过搬家公司打包成了箱子,搬进了新的办公室了,所有的箱子堆放在一间屋子里(这里所有的箱子都是相同的正方体),我们可以把这堆箱子看成一个 x*y*z 的 ...
- Fiona简介
Fiona是一个python地理空间处理库,类似于OGR
- JS框架_(JQuery.js)绚丽的3D星空动画
百度云盘: 传送门 密码:8ft8 绚丽的3D星空动画效果(纯CSS) (3D星空动画可以用作网页背景,Gary为文本文字) <!doctype html> <html lang=& ...
- 关于MySQL 处理重复数据
统计重复数据 以下我们将统计表中 first_name 和 last_name的重复记录数: mysql> SELECT COUNT(*) as repetitions, last_name, ...
- Oracle 数据自动备份 通过EXP备份
先写个批处理文件(.bat),具体如下:@echo off@echo ================================================@echo windows环境下 ...
- 全面解读php-php会话控制技术
一.PHP会话控制技术 1.为什么要使用会话控制技术? 因为http协议是无状态协议,所以同一个用户在请求同一个页面两次的时候,http协议不会认为这两次请求都来自于同一个用户,会把它们当做是两次请求 ...
- Ajax中Put和Delete请求传递参数无效的解决方法(Restful风格)
本文装载自:http://blog.csdn.net/u012737182/article/details/52831008 感谢原文作者分享 开发环境:Tomcat9.0 在使用Ajax实现R ...