每日一博 - CountDownLatch使用场景分析以及源码分析

简介: 每日一博 - CountDownLatch使用场景分析以及源码分析

d0fdb2e70e1847b2b9749789048967d3.png

并发编程常用的工具类简介


我们先看并发编程中提供的几个常用的工具类

  • CountDownLatch : CountDownLatch 用于阻塞当前 1 个或多个线程,其目的是让这些线程等待其它线程的执行完成。

可以简单将其理解为一个计数器,当初始化一个 count=n 的 CountDownLatch 对象之后,需要调用该对象的 CountDownLatch#countDown 方法来对计数器进行减值,直到计数器为 0 的时候,等待该计数器的线程才能继续执行。


但是需要注意的一点是,执行 CountDownLatch#countDown 方法的线程在执行完减值操作之后,并不会因此而阻塞。真正阻塞等待事件的是调用 CountDownLatch 对象 CountDownLatch#await 方法的线程,该线程一直会阻塞直到计数器计数变为 0 为止。

  • CyclicBarrier :CyclicBarrier 用于阻塞当前多个线程,其目的是让这些线程彼此之间相互等待,当这些线程均到达屏障后再一起往下执行
  • Semaphore:信号量,可以通过控制“许可证”的数量,来保证线程之间的配合
  • Phaser:和CyclicBarrier类似,但计数可变
  • Exchanger :两个线程交换对象
  • Condition : 可以控制线程的“等待”和“唤醒” , Object.wait()的升级版本


CountDownLatch 概述


CountDownLatch位于java.util.cucurrent包下,Java1.5被引入。同时被引入的还有其他几个工具类比如: CyclicBarrier、Semaphore、ConcurrenthashMap和BlockingQueue等。


CountDownLatch : A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.


CyclicBarrier : A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.

fe94231583a649ff8abc4c6a5c786dab.png


CountDownLatch 是一个同步计数器, 通常用于一个线程或者多个线程等待另外一组线程执行完成之前一直等待。


CountDownLatch在使用时,会初始化一个计数器,计数器的数量为线程的数量。 每一个线程执行完成以后,就会调用countDown()方法将计数器的个数-1 ,直到计数器的个为0 , 表示所有线程都执行完毕, 这个时候调用await()方法的等待线程就可以恢复继续工作了。


源码分析


19e9dae876f64b5e8d159781e9d72bfa.png


可以看出 CountDownLatch是基于Sync类实现的,而Sync继承AQS, 是AQS共享模式

6166ef19181c48b1803ef8cc86eb1dd4.png

使用场景


使用场景一: 模拟高并发并发执行(让多个线程等待)


举个例子,我们打算200个并发同时去做一笔业务,我们如果使用代码该如何实现呢?


那简单呀,老哥。

我打算去模拟200并发去做业务,那我们知道CountDownLatch 是 等countDown() 逐个减一以后,直到为0, 调用await()的线程才去工作。

那我就模拟让这200个线程调用await() ,然后等 count=0的时候 一起搞业务呗。


MMP,让我想了公司到饭点,大家一起冲刺去干饭的场景 , 这个CountDownLatch 就是那墙壁上的钟表啊 ,滴答滴答 ,就等11:45分…


我们每个人都做了准备动作(类似countDown())

时间一到(就像那await()),我们每个干饭人就是那业务线程, 那可口的大餐就是那业务

来吧,show code

package com.artisan.juc;
import java.util.concurrent.CountDownLatch;
/**
 * @author 小工匠
 * @version 1.0
 * @description: TODO
 * @date 2021/11/5 1:42
 * @mark: show me the code , change the world
 */
public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        int cnt = 10;
        CountDownLatch countDownLatch = new CountDownLatch(cnt);
        // 模拟10个并发干饭
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    // 干饭人准备完毕……干饭人都阻塞在这,等待号令(cnt=0)
                    countDownLatch.await();
                    System.out.println("编号:" + Thread.currentThread().getName() + " 开始干饭...." + System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            // 模拟一部分业务耗时
            Thread.sleep(10);
            // 每个线程调用一次,cnt每次减一,直到为0,就是干饭信号
            countDownLatch.countDown();
            System.out.println("countDown执行一次,个数:" + (--cnt));
        }
    }
}

6c87a836ba7f4a1fb7abb969986bca04.png


可以看到,我们通过CountDownLatch#await()方法,让多个业务线程启动后阻塞等待, 等待主线程调用CountDownLatch#count() 方法将计数器减为0以后,让所有的业务线程一起并发运行,这样就实现了我们多个线程并发的目的。


049ced37ca5947df976056093baf6a8e.gif



使用场景二: 模拟异步执行后回到主线程的业务(让一个线程等待)

举个例子,我们有一些依赖关系的业务

String  str = method1();  // 耗时1S 
String  str2 = method2();// 耗时2S 
method3(str, str2); 


这mmp ,数据有依赖呀, 当然了,你可以用CompletableFuture优化,我们这里不讨论它嘛



3f621745e27344cd9c03d5e80c75a168.png


我们看看CountDownLacth该怎么玩?

分析一下这个业务场景, 让method 1 和 method 2 异步执行,然后执行完以后再回到主线程,取到返回结果汇总呗

我们简化下,不获取返回结果

package com.artisan.juc;
import java.util.concurrent.*;
/**
 * @author 小工匠
 * @version 1.0
 * @description: TODO
 * @date 2021/11/5 1:42
 * @mark: show me the code , change the world
 */
public class CountDownLatchTest2 {
    public static void main(String[] args) throws InterruptedException {
        // 2 为线程个数
        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
                countDownLatch.countDown();
                System.out.println(Thread.currentThread().getName() + " --执行结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
                countDownLatch.countDown();
                System.out.println(Thread.currentThread().getName() + "**执行结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        countDownLatch.await();
        System.out.println("主线业务继续执行");
    }
}

f3022a6d7559457b853c2023d4071d0a.png


我们可以看到: 在每个线程 完成的最后一行加上CountDownLatch#countDown(),让计数器-1;当所有线程完成-1,主线程之前阻塞在这里 countDownLatch.await(); ,直到计数器减到0后,往下继续执行自己的业务。


相关文章
|
机器学习/深度学习 数据采集 开发者
基于机器学习的垃圾邮件过滤系统
基于机器学习的垃圾邮件过滤系统
822 0
|
12月前
|
监控 安全 网络安全
防患于未然:如何构建抵御.sstop勒索病毒的防线
近年来,勒索病毒攻击事件频发,给个人和企业带来了巨大的安全威胁。其中,.sstop勒索病毒以其独特的加密手段和传播途径,成为网络安全领域的一大隐患。本文将详细介绍.sstop勒索病毒的特点,以及如何恢复被其加密的数据文件和预防措施。
|
自然语言处理 运维 开发工具
深入探讨了 NeoVim 相较于传统 Vim 的优势,包括更好的扩展性、现代化的界面和用户体验、多语言编程支持、强大的异步处理能力、更好的协作支持、持续的更新和改进、活跃的社区以及与现代开发工具的集成
本文深入探讨了 NeoVim 相较于传统 Vim 的优势,包括更好的扩展性、现代化的界面和用户体验、多语言编程支持、强大的异步处理能力、更好的协作支持、持续的更新和改进、活跃的社区以及与现代开发工具的集成。通过命令对比,展示了两者在启动、配置、模式切换、移动编辑、搜索替换、插件管理、文件操作、窗口缓冲区管理和高级功能等方面的差异。总结部分强调了 NeoVim 在多个方面的显著优势,解释了为什么越来越多的运维人员选择 NeoVim。
930 3
|
设计模式 架构师 Java
2024到来!一到五年Java工程师想跳槽,大环境不好,怎么破?
会不会因为裁员潮,市场上工作机会比往年跳槽季更少,同时求职者因为失业或裁员潮带来的恐慌心理,很多人在找工作时更怕错过机会而不做过多思考和选择就入职。这样的形势下跳槽或者求职时,该如何判断和做出选择?
|
Java 测试技术
Java SpringBoot Test 单元测试中包括多线程时,没跑完就结束了
Java SpringBoot Test 单元测试中包括多线程时,没跑完就结束了
314 0
|
Java 开发者
Java中什么是竞态条件
【8月更文挑战第10天】Java中什么是竞态条件
269 0
|
关系型数据库 MySQL 索引
MySQL是如何通过索引查询数据的?具体流程是怎样的?底层原理是什么?
MySQL是如何通过索引查询数据的?具体流程是怎样的?底层原理是什么?
634 0
|
缓存 Java
Java中四种引用类型(强、软、弱、虚)
Java中四种引用类型(强、软、弱、虚)
|
负载均衡 Dubbo 前端开发
从原理到操作,让你在 Apache APISIX 中代理 Dubbo3 服务更便捷
本文为大家介绍了如何借助 Apache APISIX 实现 Dubbo Service 的代理,通过引入 dubbo-proxy 插件便可为 Dubbo 框架的后端系统构建更简单更高效的流量链路。
1206 79
从原理到操作,让你在 Apache APISIX 中代理 Dubbo3 服务更便捷
|
XML Java API
一款直击痛点的优秀http框架,让我超高效完成了第三方接口的对接
一款直击痛点的优秀http框架,让我超高效完成了第三方接口的对接
346 0