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解析的更多相关文章

  1. 死磕 java同步系列之CyclicBarrier源码解析——有图有真相

    问题 (1)CyclicBarrier是什么? (2)CyclicBarrier具有什么特性? (3)CyclicBarrier与CountDownLatch的对比? 简介 CyclicBarrier ...

  2. 死磕 java同步系列之Phaser源码解析

    问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这 ...

  3. 死磕 java同步系列之StampedLock源码解析

    问题 (1)StampedLock是什么? (2)StampedLock具有什么特性? (3)StampedLock是否支持可重入? (4)StampedLock与ReentrantReadWrite ...

  4. 【死磕 Spring】----- IOC 之解析 bean 标签:开启解析进程

    原文出自:http://cmsblogs.com import 标签解析完毕了,再看 Spring 中最复杂也是最重要的标签 bean 标签的解析过程. 在方法 parseDefaultElement ...

  5. 【死磕 Spring】—– IOC 之解析Bean:解析 import 标签

    原文出自:http://cmsblogs.com 在博客[死磕Spring]----- IOC 之 注册 BeanDefinition中分析到,Spring 中有两种解析 Bean 的方式.如果根节点 ...

  6. 死磕 java同步系列之volatile解析

    问题 (1)volatile是如何保证可见性的? (2)volatile是如何禁止重排序的? (3)volatile的实现原理? (4)volatile的缺陷? 简介 volatile可以说是Java ...

  7. 死磕 java线程系列之线程池深入解析——普通任务执行流程

    (手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本. 注:线程池源码部分如无特殊说明均指ThreadPoolExecutor类. 简介 前面我们一起学习了Java中 ...

  8. 死磕 java线程系列之线程池深入解析——定时任务执行流程

    (手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本. 注:本文基于ScheduledThreadPoolExecutor定时线程池类. 简介 前面我们一起学习了普通 ...

  9. 死磕 java同步系列之Semaphore源码解析

    问题 (1)Semaphore是什么? (2)Semaphore具有哪些特性? (3)Semaphore通常使用在什么场景中? (4)Semaphore的许可次数是否可以动态增减? (5)Semaph ...

随机推荐

  1. .net post 字符串含有+号的时候,加号会变成空格 处理方法

    value= value.Replace("+", "%2B");  替换加号

  2. @ApiParam和@RequestHeader接收参数的区别

    一.代码 @ApiOperation(value = "查询学生信息") @GetMapping(value = "/select/student") @Api ...

  3. python实现一个朴素贝叶斯分类方法

    1.公式 上式中左边D是需要预测的测试数据属性,h是需要预测的类:右边式子分子是属性的条件概率和类别的先验概率,可以从统计训练数据中得到,分母对于所有实例都一样,可以不考虑,所有只需 ,返回最大概率的 ...

  4. python脚本中selenium启动浏览器报错os.path.basename(self.path), self.start_error_message) selenium.common.excep

    在python脚本中,使用selenium启动浏览器报错,原因是未安装浏览器驱动,报错内容如下: # -*- coding:utf-8 -*-from selenium import webdrive ...

  5. javascript 六种数据类型

    js的数据类型和常见隐式转化逻辑. 一.六种数据类型 原始类型(基本类型):按值访问,可以操作保存在变量中实际的值.原始类型汇总中null和undefined比较特殊. 引用类型:引用类型的值是保存在 ...

  6. Java Optional orElse() 和 orElseGet() Optional.flatMap()和Optional.map()区别

    Java Optional 的 orElse() 和 orElseGet() 的区别 1. 接收的参数不同 orElse()方法以一个自定义类型的数据作为参数 public T orElse(T t) ...

  7. [Java]分解算术表达式一

    源码: package com.hy; import java.io.BufferedReader; import java.io.IOException; import java.io.InputS ...

  8. Mac 安装 Novicat

    https://blog.csdn.net/jor_ivy/article/details/81323199 详细见这篇文章

  9. Anaconda 配置 Python 环境

    原文地址:Anaconda 配置 Python 环境 0x00 环境 Anaconda: 2019.03 Python: 3.6.8 0x01 Linux 安装 Anaconda 交互安装 Anaco ...

  10. 通过jenkins-Python在后台操作Jenkins构建job

    最近要开发1个接口,接收到1个指令后自动触发自动化测试,虽然也可以通过shell命令做这一步,但因为目前所有构建自动化的的动作都通过jenkins完成,所以想要尝试能不能用python去控制jenki ...