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. Flask之请求上下文流程图

    整理一下Flask请求上下文流程导思流程图,如果错误,请指出.

  2. SessionState的几种设置

    SessionState:   web Form 网页是基于HTTP的,它们没有状态, 这意味着它们不知道所有的请求是否来自同一台客户端计算机,网页是受到了破坏,以及是否得到了刷新,这样就可能造成信息 ...

  3. C++:关键字explicit的用法

    预测下面C++程序的输出: #include <iostream> using namespace std; class Complex { private: double real; d ...

  4. Open Cascade:计算体积

    TopoDS_Shape shape; ... GProp_GProps System; BRepGProp::LinearProperties(shape, System); BRepGProp:: ...

  5. Eclipse 导入逆向工程

    相关配置 生成 生产前 生成后

  6. CMS 与 框架

    Framework:框架.是整合的工具集,基于编程语言.可以帮助我们快速开发网站.比较常见的是J2EE(基于Java),Symfony2(基于PHP),Django(基于Python),Ruby on ...

  7. Andorid获取状态栏高度

    在应用开发中,有时我们需要用代码计算布局的高度,可能需要减去状态栏(status bar)的高度.状态栏高度定义在Android系统尺寸资源中status_bar_height,但这并不是公开可直接使 ...

  8. 实体字符转换,同样变量密码加盐MD5后生成的加密字符串不同解决办法 (原)

    我是首次登录系统自动生成一个密码,格式大概是:   abcd1234&  这种格式 , 比如加密规则就是一个 MD5() 然后,首次账号密码登录,输入密码 abcd1234&,一直提示 ...

  9. C++入门经典-例2.16-隐式类型转换

    1:代码如下: // 2.16.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> using ...

  10. jQuery Validate 验证成功时的提示信息

    大多数时候我们使用validate进行前台验证的时候,都是验证错误的时候给出提示信息,最近在做一个项目的时候,想这验证成功后给出一个验证成功的提示.于是在网上找了一些资料. <!doctype ...