【并发编程】CountDownLatch

       📝个人主页:五敷有你      

 🔥系列专栏:并发编程

⛺️稳中求进,晒太阳

CountDownLatch 概念

CountDownLatch可以使一个获多个线程等待其他线程各自执行完毕后再执行。

CountDownLatch 定义了一个计数器,和一个阻塞队列, 当计数器的值递减为0之前,阻塞队列里面的线程处于挂起状态,当计数器递减到0时会唤醒阻塞队列所有线程,这里的计数器是一个标志,可以表示一个任务一个线程,也可以表示一个倒计时器,CountDownLatch可以解决那些一个或者多个线程在执行之前必须依赖于某些必要的前提业务先执行的场景。

CountDownLatch 常用方法说明

CountDownLatch(int count); //构造方法,创建一个值为count 的计数器。 ​ await();//阻塞当前线程,将当前线程加入阻塞队列。 ​ await(long timeout, TimeUnit unit);//在timeout的时间之内阻塞当前线程,时间一过则当前线程可以执行, ​ countDown();//对计数器进行递减1操作,当计数器递减至0时,当前线程会去唤醒阻塞队列里的所有线程。

用CountDownLatch 来优化我们的报表统计

功能现状

运营系统有统计报表、业务为统计每日的用户新增数量、订单数量、商品的总销量、总销售额......等多项指标统一展示出来,因为数据量比较大,统计指标涉及到的业务范围也比较多,所以这个统计报表的页面一直加载很慢,所以需要对统计报表这块性能需进行优化。

问题分析

统计报表页面涉及到的统计指标数据比较多,每个指标需要单独的去查询统计数据库数据,单个指标只要几秒钟,但是页面的指标有10多个,所以整体下来页面渲染需要将近一分钟。

解决方案

任务时间长是因为统计指标多,而且指标是串行的方式去进行统计的,我们只需要考虑把这些指标从串行化的执行方式改成并行的执行方式,那么整个页面的时间的渲染时间就会大大的缩短, 如何让多个线程同步的执行任务,我们这里考虑使用多线程,每个查询任务单独创建一个线程去执行,这样每个统计指标就可以并行的处理了。

要求

因为主线程需要每个线程的统计结果进行聚合,然后返回给前端渲染,所以这里需要提供一种机制让主线程等所有的子线程都执行完之后再对每个线程统计的指标进行聚合。 这里我们使用CountDownLatch 来完成此功能。

模拟代码

1、分别统计4个指标用户新增数量、订单数量、商品的总销量、总销售额;

2、假设每个指标执行时间为3秒。如果是串行化的统计方式那么总执行时间会为12秒。

3、我们这里使用多线程并行,开启4个子线程分别进行统计

4、主线程等待4个子线程都执行完毕之后,返回结果给前端。

​//用于聚合所有的统计指标private static Map map=new HashMap();//创建计数器,这里需要统计4个指标private static CountDownLatch countDownLatch=new CountDownLatch(4);
​public static void main(String[] args) {
​//记录开始时间long startTime=System.currentTimeMillis();
​Thread countUserThread=new Thread(new Runnable() {public void run() {try {System.out.println("正在统计新增用户数量");Thread.sleep(3000);//任务执行需要3秒map.put("userNumber",1);//保存结果值countDownLatch.countDown();//标记已经完成一个任务System.out.println("统计新增用户数量完毕");} catch (InterruptedException e) {e.printStackTrace();}
​}});Thread countOrderThread=new Thread(new Runnable() {public void run() {try {System.out.println("正在统计订单数量");Thread.sleep(3000);//任务执行需要3秒map.put("countOrder",2);//保存结果值countDownLatch.countDown();//标记已经完成一个任务System.out.println("统计订单数量完毕");} catch (InterruptedException e) {e.printStackTrace();}
​}});
​Thread countGoodsThread=new Thread(new Runnable() {public void run() {try {System.out.println("正在商品销量");Thread.sleep(3000);//任务执行需要3秒map.put("countGoods",3);//保存结果值countDownLatch.countDown();//标记已经完成一个任务System.out.println("统计商品销量完毕");} catch (InterruptedException e) {e.printStackTrace();}
​}});
​Thread countmoneyThread=new Thread(new Runnable() {public void run() {try {System.out.println("正在总销售额");Thread.sleep(3000);//任务执行需要3秒map.put("countmoney",4);//保存结果值countDownLatch.countDown();//标记已经完成一个任务System.out.println("统计销售额完毕");} catch (InterruptedException e) {e.printStackTrace();}
​}});//启动子线程执行任务countUserThread.start();countGoodsThread.start();countOrderThread.start();countmoneyThread.start();
​try {//主线程等待所有统计指标执行完毕countDownLatch.await();long endTime=System.currentTimeMillis();//记录结束时间System.out.println("------统计指标全部完成--------");System.out.println("统计结果为:"+map.toString());System.out.println("任务总执行时间为"+(endTime-startTime)/1000+"秒");
​} catch (InterruptedException e) {e.printStackTrace();}
​
​}
​

执行结果

CountDownLatch实现原理

1、创建计数器

当我们调用CountDownLatch countDownLatch=new CountDownLatch(4) 时候,此时会创建一个AQS的同步队列,并把创建CountDownLatch 传进来的计数器赋值给AQS队列的 state,所以state的值也代表CountDownLatch所剩余的计数次数;

  public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);//创建同步队列,并设置初始计数器值}

2、阻塞线程

当我们调用countDownLatch.wait()的时候,会创建一个节点,加入到AQS阻塞队列,并同时把当前线程挂起。

  public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);}

判断计数器是技术完毕,未完毕则把当前线程加入阻塞队列

  public final void acquireSharedInterruptibly(int arg)throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();//锁重入次数大于0 则新建节点加入阻塞队列,挂起当前线程if (tryAcquireShared(arg) < 0)doAcquireSharedInterruptibly(arg);}

构建阻塞队列的双向链表,挂起当前线程

 private void doAcquireSharedInterruptibly(int arg)throws InterruptedException {//新建节点加入阻塞队列final Node node = addWaiter(Node.SHARED);boolean failed = true;try {for (;;) {//获得当前节点pre节点final Node p = node.predecessor();if (p == head) {int r = tryAcquireShared(arg);//返回锁的stateif (r >= 0) {setHeadAndPropagate(node, r);p.next = null; // help GCfailed = false;return;}}//重组双向链表,清空无效节点,挂起当前线程if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}}

3、计数器递减

当我们调用countDownLatch.down()方法的时候,会对计数器进行减1操作,AQS内部是通过释放锁的方式,对state进行减1操作,当state=0的时候证明计数器已经递减完毕,此时会将AQS阻塞队列里的节点线程全部唤醒。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/296728.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【多线程】震惊~这是我见过最详细的ReentrantLock的讲解

一.与synchronized相比ReentrantLock具有以下四个特点: 可中断&#xff1a;synchronized只能等待同步代码块执行结束&#xff0c;不可以中断&#xff0c;强行终断会抛出异常, 而reentrantlock可以调用线程的interrupt方法来中断等待&#xff0c;继续执行下面的代码。 在获取锁…

【C++学习】哈希表的底层实现及其在unordered_set与unordered_map中的封装

文章目录 1. unordered系列关联式容器1.1 unordered_map1.2 unordered_set1.3.底层结构 2.哈希2.1哈希概念2.2哈希冲突2.3 哈希函数2.4 哈希冲突解决2.4.1闭散列2.4.1开散列2.5开散列与闭散列比较 3.哈希的模拟实现1. 模板参数列表2. 迭代器的实现3. 增加通过key获取value操作4…

基于 Quartz.NET 可视化任务调度平台 QuartzUI

一、简介 QuartzUI 是基于 Quartz.NET3.0 的定时任务 Web 可视化管理&#xff0c;Docker 打包开箱即用、内置 SQLite 持久化、语言无关、业务代码零污染、支持 RESTful 风格接口、傻瓜式配置、异常请求邮件通知等。 二、部署 QuartzUI 从 2022 年到现在没有提交记录&#xf…

YOLO火灾烟雾检测数据集:20000多张,yolo标注完整

YOLO火灾烟雾检测数据集&#xff1a;一共20859张图像&#xff0c;yolo标注完整&#xff0c;部分图像应用增强 适用于CV项目&#xff0c;毕设&#xff0c;科研&#xff0c;实验等 需要此数据集或其他任何数据集请私信

zabbix源码安装

目录 一.安装php和nginx客户端环境 二.修改php配置 三.修改nginx配置文件 四.下载并编译zabbix 五.创建zabbix需要的用户及组 六.安装编译需要的依赖 七.配置zabbix文件 八.数据库配置 九.配置zabbix 十.web界面部署 十一.遇到无法创建配置文件 十二.登录zabbix 前…

基于深度学习的条形码二维码检测系统(网页版+YOLOv8/v7/v6/v5代码+训练数据集)

摘要&#xff1a;本文深入研究了基于YOLOv8/v7/v6/v5的条形码二维码检测系统。核心采用YOLOv8并整合了YOLOv7、YOLOv6、YOLOv5算法&#xff0c;进行性能指标对比&#xff1b;详述了国内外研究现状、数据集处理、算法原理、模型构建与训练代码&#xff0c;及基于Streamlit的交互…

.Websalm勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复

导言&#xff1a; 在数字化时代&#xff0c;网络安全问题日益凸显&#xff0c;其中勒索病毒作为一种新型的电脑病毒&#xff0c;以其独特的传播方式和恶劣的性质&#xff0c;给广大用户带来了巨大的困扰。近期&#xff0c;Websalm勒索病毒成为了公众关注的焦点&#xff0c;其强…

刷题DAY44 | 完全背包问题 LeetCode 518-零钱兑换 II 377-组合总和 Ⅳ

完全背包问题模版 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09;&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 完全背包和01背包问题…

【Oracle篇】expdp/impdp高效完成全部生产用户的全库迁移(第四篇,总共四篇)

☘️博主介绍☘️&#xff1a; ✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux&#xff0c;也在扩展大数据方向的知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章&#xff0c;并且也会默默的点赞收藏加关注❣️❣️…

C++中的vector与C语言中的数组的区别

C中的vector和C语言中的数组在很多方面都有所不同&#xff0c;以下是它们之间的一些主要区别&#xff1a; 大小可变性&#xff1a; vector是C标准模板库&#xff08;STL&#xff09;提供的动态数组容器&#xff0c;它的大小可以动态增长或减少。这意味着你可以在运行时添加或删…

物联网实战--入门篇之(七)嵌入式-MQTT

目录 一、MQTT简介 二、MQTT使用方法 三、MQTT驱动设计 四、代码解析 五、使用过程 六、总结 一、MQTT简介 MQTT因为其轻量、高效和稳定的特点&#xff0c;特别适合作为物联网系统的数据传输协议&#xff0c;已经成为物联网事实上的通信标准了。关于协议的具体内容看看这…

校园局域网钓鱼实例

Hello &#xff01; 我是"我是小恒不会java" 本文仅作为针对普通同学眼中的网络安全&#xff0c;设计的钓鱼案例也是怎么简陋怎么来 注&#xff1a;本文不会外传代码&#xff0c;后端已停止使用&#xff0c;仅作为学习使用 基本原理 内网主机扫描DNS劫持前端模拟后端…

图论基础(python蓝桥杯)

图的基本概念 图的种类 怎么存放图呢&#xff1f; 优化 DFS 不是最快/最好的路&#xff0c;但是能找到一条连通的道路。&#xff08;判断两点之间是不是连通的&#xff09; 蓝桥3891 import os import sys sys.setrecursionlimit(100000) # 请在此输入您的代码 n, m map(int,…

【Frida】【Android】 07_爬虫之网络通信库HttpURLConnection

&#x1f6eb; 系列文章导航 【Frida】【Android】01_手把手教你环境搭建 https://blog.csdn.net/kinghzking/article/details/136986950【Frida】【Android】02_JAVA层HOOK https://blog.csdn.net/kinghzking/article/details/137008446【Frida】【Android】03_RPC https://bl…

SQL server 查询数据库中所有的表名及行数

SQL server 查询数据库中所有的表名及行数 select a.name,b.rows from sysobjects as ainner join sysindexes as bon a.id b.id where (a.type u)and (b.indid in (0, 1)) and b.rows<50 and b.rows>20 order by a.name, b.rows desc;

图像处理_积分图

目录 1. 积分图算法介绍 2. 基本原理 2.1 构建积分图 2.2 使用积分图 3. 举个例子 1. 积分图算法介绍 积分图算法是图像处理中的经典算法之一&#xff0c;由Crow在1984年首次提出&#xff0c;它是为了在多尺度透视投影中提高渲染速度。 积分图算法是一种快速计算图像区域和…

Ceph分布式存储系统以及高可用原理

Ceph分布式存储系统以及高可用原理 1. Ceph原理和架构1.1 分布式存储系统抽象1.2 Ceph基本组件 2 Ceph中的策略层2.1 CRUSH进行数据分发和定位2.2 PG(Placement Group): 集群管理的基本单元2.3 PG的代理primary OSD2.4 轻量级的集群元数据ClusterMap2.5 对PG的罗辑分组&#xf…

观察和配置MAC地址表

目录 原理概述 实验目的 实验内容 实验拓扑 ​编辑1&#xff0e;基本配置 2.观察正常状态时的MAC地址表 4.配置静态MAC地址表项 原理概述 MAC 地址表是交换机的一个核心组成部分&#xff0c;交换机主要是根据 MAC 地址表来进行帧的转发的。交换机对帧的转发操作行为一共有…

车道线检测_Canny算子边缘检测_1

Canny算子边缘检测&#xff08;原理&#xff09; Canny算子边缘检测是一种经典的图像处理算法&#xff0c;由John F. Canny于1986年提出&#xff0c;用于精确、可靠地检测数字图像中的边缘特征。该算法设计时考虑了三个关键目标&#xff1a;低错误率&#xff08;即尽可能多地检…

【漏洞复现】WordPress Plugin LearnDash LMS 敏感信息暴漏

漏洞描述 WordPress和WordPress plugin都是WordPress基金会的产品。WordPress是一套使用PHP语言开发的博客平台。该平台支持在PHP和MySQL的服务器上架设个人博客网站。WordPress plugin是一个应用插件。 WordPress Plugin LearnDash LMS 4.10.2及之前版本存在安全漏洞&#x…