Java多线程实战-CountDownLatch模拟压测实现

🏷️个人主页:牵着猫散步的鼠鼠 

🏷️系列专栏:Java全栈-专栏

🏷️本系列源码仓库:多线程并发编程学习的多个代码片段(github)

🏷️个人学习笔记,若有缺误,欢迎评论区指正 

目录

前言

CountDownLatch 的基本原理

CountDownLatch 基本用法

CountDownLatch 示例

使用CountDownLatch实现压测

总结


前言

当多个线程需要协调和同步执行任务时,Java 中的 CountDownLatch(倒计时器)是一个常用的工具类。它可以帮助开发者实现线程之间的同步,确保某些线程在其他线程完成任务后再继续执行。本文将介绍 CountDownLatch 的基本原理、用法以及示例代码,最后会使用CountDownLatch完成一个简单的压测实现。

CountDownLatch 的基本原理

CountDownLatch 是基于计数器的原理实现的,它内部维护了一个整型的计数器。创建一个 CountDownLatch 对象时,需要指定一个初始计数值,该计数值表示需要等待的线程数量。每当一个线程完成了其任务,它调用 CountDownLatch 的 countDown() 方法,计数器的值就会减一。当计数器的值变成 0 时,等待的线程就会被唤醒,继续执行它们的任务。

CountDownLatch 基本用法

CountDownLatch 在多线程编程中有广泛的应用场景,例如主线程等待所有子线程完成任务后再继续执行,多个线程协同完成一个任务等。以下是 CountDownLatch 的基本用法:

创建 CountDownLatch 对象,并指定初始计数值。

CountDownLatch latch = new CountDownLatch(3); // 初始计数值为 3

创建需要等待的线程,线程完成任务后调用 countDown() 方法。 

Runnable task = new Runnable() {@Overridepublic void run() {// 线程任务逻辑// ...latch.countDown(); // 完成任务后调用 countDown()}
};

创建等待线程,等待计数器的值变成 0 后再继续执行。

try {latch.await(); // 等待计数器的值变成 0// 继续执行需要等待的线程后续逻辑
} catch (InterruptedException e) {// 处理中断异常e.printStackTrace();
}

CountDownLatch 示例

下面再通过一个简单的示例代码来演示 CountDownLatch 的用法。假设有一个需求,需要三个工人协同完成一项任务,主线程需要等待三个工人完成任务后才能继续执行。示例代码如下:

import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) {final int workerCount = 3;final CountDownLatch latch = new CountDownLatch(workerCount);// 创建并启动三个工人线程for (int i = 0; i < workerCount; i++) {Worker worker = new Worker(latch, "Worker " + (i + 1));new Thread(worker).start();}try {System.out.println("Main thread is waiting for workers to finish...");latch.await(); // 主线程等待三个工人线程完成任务System.out.println("All workers have finished, main thread continues...");} catch (InterruptedException e) {e.printStackTrace();}}static class Worker implements Runnable {private final CountDownLatch latch;private final String name;public Worker(CountDownLatch latch, String name) {this.latch = latch;this.name = name;}@Overridepublic void run() {System.out.println(name + " starts working...");// 模拟工作耗时try {Thread.sleep((long) (Math.random() * 10000));} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + " finishes working.");latch.countDown(); // 工人完成任务后调用 countDown()}}
}

在上述代码中,首先创建了一个 CountDownLatch 对象,并指定初始计数值为 3。然后创建了三个工人线程并启动,每个工人线程模拟完成一项工作后调用 countDown() 方法,计数器的值减一。最后,主线程调用 await() 方法等待计数器的值变成 0,表示所有工人都完成任务后再继续执行。

运行该程序后,输出结果如下:

Worker 1 starts working...  
Worker 2 starts working...  
Worker 3 starts working...  
Main thread is waiting for workers to finish...  
Worker 2 finishes working.  
Worker 1 finishes working.  
Worker 3 finishes working.  
All workers have finished, main thread continues...

可以看到,主线程在三个工人线程完成任务后才继续执行,并且所有工人线程的完成顺序是不确定的,但主线程会一直等待直到所有工人完成任务。

扩展:上面的worker线程运行是没有顺序的,我们可以使用join()来使线程有序等待上一个线程运行结束。

public static void main(String[] args) throws InterruptedException {final int workerCount = 3;final CountDownLatch latch = new CountDownLatch(workerCount);System.out.println("Main thread is waiting for workers to finish...");// 创建并启动三个工人线程for (int i = 0; i < workerCount; i++) {Worker worker = new Worker(latch, "Worker " + (i + 1));Thread t = new Thread(worker);t.start();t.join(); // 等待上一个线程执行完成}try {latch.await(); // 主线程等待三个工人线程完成任务System.out.println("All workers have finished, main thread continues...");} catch (InterruptedException e) {e.printStackTrace();}
}

输出结果:

Main thread is waiting for workers to finish...
Worker 1 starts working...
Worker 1 finishes working.
Worker 2 starts working...
Worker 2 finishes working.
Worker 3 starts working...
Worker 3 finishes working.
All workers have finished, main thread continues...

CountDownLatch 和 join 的作用和使用方式不同。CountDownLatch 用于等待多个线程完成任务后再继续执行,而 join 用于等待一个线程执行完毕后再继续执行。另外,CountDownLatch 是基于计数器的实现,可以灵活地控制线程的数量和完成顺序;而 join 方法只能等待单个线程执行完毕。 

使用CountDownLatch实现压测

我们以上讲解了CountDownLatch的简单使用,接下来就利用CountDownLatch实现一个简单的压测程序,小伙伴可以在此基础上进行扩展

@Slf4j
public class concurrenceTest {public static void concurrenceTest() {/*** 模拟高并发情况代码*/final AtomicInteger atomicInteger = new AtomicInteger(0);final CountDownLatch countDownLatch = new CountDownLatch(1000); // 相当于计数器,当所有都准备好了,再一起执行,模仿多并发,保证并发量final CountDownLatch countDownLatch2 = new CountDownLatch(1000); // 保证所有线程执行完了再打印atomicInteger的值ExecutorService executorService = Executors.newFixedThreadPool(10);try {for (int i = 0; i < 1000; i++) {executorService.submit(() -> {try {countDownLatch.await(); //一直阻塞当前线程,直到计时器的值为0,保证同时并发} catch (InterruptedException e) {log.error(e.getMessage(), e);}//每个线程增加1000次,每次加1for (int j = 0; j < 1000; j++) {// 用业务代码替换atomicInteger.incrementAndGet();}countDownLatch2.countDown();});countDownLatch.countDown();}countDownLatch2.await();// 保证所有线程执行完executorService.shutdown();log.info("atomicInteger的值为:{}", atomicInteger.get());} catch (Exception e) {log.error(e.getMessage(), e);}}public static void main(String[] args) {concurrenceTest();}
}

在上述代码中,我们循环添加1000个任务到executorService 线程池中, 并让每一个线程在加入到线程池后阻塞等待,每加入一个线程countDownLatch就减一,直到全部线程都加入到线程池中,线程池中的线程会停止阻塞开始执行,也就模拟了并发场景,可以自行调整线程池的线程数大小来调节压测压力。

总结

CountDownLatch 是一个非常实用的线程同步工具,在多线程编程中有着广泛的应用。它基于计数器的原理实现,通过等待计数器的值变成 0 来实现线程的同步和协作。在使用 CountDownLatch 时,需要注意初始计数值的设定和 countDown() 和 await() 方法的使用。 

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习 精选笔记(13.2)深度卷积神经网络-AlexNet模型

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

单片机学到什么程度才可以去工作?

单片机学到什么程度才可以去工作? 如果没有名校或学位的加持&#xff0c;你还得再努力一把&#xff0c;才能从激烈的竞争中胜出。以下这些技能可以给你加分&#xff0c;你看情况学&#xff0c;不同行业对这些组件会有取舍: . Cortex-M内核:理解MCU内核各部件的工作机制&#…

如何优化使用Nginx

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容数据压缩负载均衡安装OpenResty或ngx_http_lua_module配置Nginx以启用Lua编写Lua脚本配置upstream块以使用Lua变量测试配置 合并请求1. 确保SSI模块已启用2. 配置Nginx以使用SSI3. 使用SSI指令4. 重新加载或重启Nginx 集成…

Python爬虫与数据可视化源码免费领取

引言 作为一名在软件技术领域深耕多年的专业人士&#xff0c;我不仅在软件开发和项目部署方面积累了丰富的实践经验&#xff0c;更以卓越的技术实力获得了&#x1f3c5;30项软件著作权证书的殊荣。这些成就不仅是对我的技术专长的肯定&#xff0c;也是对我的创新精神和专业承诺…

【leetcode-53最大子数组和】

题目&#xff1a; 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。子数组是数组中的一个连续部分。 示例 1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,-5,4] …

MySQL知识点极速入门

准备SQL 创建数据库&#xff1a; 创建一个名为emptest的数据库 create database emptest; use emptest; 创建数据表&#xff1a; 设计一张员工信息表&#xff0c;要求如下&#xff1a; 1. 编号&#xff08;纯数字&#xff09; 2. 员工工号 (字符串类型&#xff0c;长度不超…

Windows10中配置并使用nvidia-smi

1. 问题 当在window10系统中使用nvidia-smi命令时&#xff1a; 会得到提示&#xff1a;nvidia-smi不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。 注&#xff1a;其实安装NVIDIA控制面板时&#xff0c;软件已内置安装了nvidia-smi.exe&#xff0c;我们只需…

文件包含漏洞(input、filter、zip)

一、PHP://INPUT php://input可以访问请求的原始数据的只读流&#xff0c;将post请求的数据当作php代码执行。当传入的参数作为文件名打开时&#xff0c;可以将参数设为php://input,同时post想设置的文件内容&#xff0c;php执行时会将post内容当作文件内容。从而导致任意代码…

【Java刷题篇】串联所有单词的子串

这里写目录标题 &#x1f4c3;1.题目&#x1f4dc;2.分析题目&#x1f4dc;3.算法原理&#x1f9e0;4.思路叙述✍1.进窗口✍2.判断有效个数✍3.维护窗口✍4.出窗口 &#x1f4a5;5.完整代码 &#x1f4c3;1.题目 力扣链接: 串联所有单词的子串 &#x1f4dc;2.分析题目 阅…

长连接技术

个人学习记录&#xff0c;欢迎指正 1.轮询 1.1 轮询的形式 短连接轮询 前端每隔一段时间向服务端发起一次Http请求来获取数据。 const shortPolling () > { const intervalHandler setInterval(() > {fetch(/xxx/yyy).then(response > response.json()).then(respo…

企业计算机服务器中了devicdata勒索病毒怎么办,devicdata勒索病毒解密工具流程

随着科学技术的不断发展与应用&#xff0c;越来越多的企业开始利用网络开展各项工作业务&#xff0c;网络为企业的生产运营提供了极大便利&#xff0c;大大提高了生产运营效率&#xff0c;同时也为企业的发展规划带来不错的契机。但网络是一把双刃剑&#xff0c;网络在为人们提…

HAProxy高性能负载均衡器

一、HAProxy基础知识 &#xff08;一&#xff09;HAProxy概述 HAProxy是一款基于事件驱动、单进程模型设计的四层与七层负载均衡器&#xff0c;它能够在TCP/UDP层面以及HTTP(S)等应用层协议上实现高效的流量分发。HAProxy不仅适用于Web服务器负载均衡&#xff0c;还能应用于数据…

AI大浪潮,怎能少了国产HBM内存?

据有关报道显示&#xff0c;武汉新芯半导体制造有限公司&#xff08;XMC&#xff09;正在启动一项专注于开发和生产高带宽内存&#xff08;HBM&#xff09;的项目。 HBM作为一种关键的DRAM类型&#xff0c;对于人工智能&#xff08;AI&#xff09;和高性能计算&#xff08;HPC&…

腾讯云轻量应用服务器2核4G5M代表什么意思?

腾讯云服务器2核4G5M带宽配置是代表什么&#xff1f;代表2核CPU、4G内存、5M公网带宽&#xff0c;这是一款轻量应用服务器&#xff0c;系统盘为60GB SSD云硬盘&#xff0c;活动页面 txybk.com/go/txy 活动打开如下图&#xff1a; 腾讯云2核4G5M服务器 如上图所示&#xff0c;这…

智慧公厕建设的主要目标是什么?

随着城市化进程的不断推进&#xff0c;公共厕所作为城市基础设施的重要组成部分&#xff0c;也变得越来越重要。为了提升公共厕所的管理水平、提供更好的服务质量&#xff0c;智慧公厕应运而生。智慧公厕的建设旨在通过信息化手段实现公共厕所的全面感知监测&#xff0c;实现公…

Jmeter文件上传不成功问题

前言 最近好忙呀&#xff0c;项目上线然后紧接着又客户培训了&#xff0c;由于项目有个模块全是走配置的&#xff0c;所以导致问题不断&#xff0c;近期要培训为了保障培训时客户同时操作的情况&#xff0c;所以把我从功能端抽出来做压测了&#xff0c;之前安排了2个同事写压测…

力扣24. 两两交换链表中的节点

新建虚拟头节点&#xff0c;用3个指针记录前3个节点&#xff0c;然后再相互赋值指向&#xff0c;再移动当前节点&#xff0c;当前节点所在的位置&#xff0c;只能交换该节点的后两个节点&#xff08;所以必须建立虚拟头节点&#xff0c;才能操作第1&#xff0c;2个节点&#xf…

ts版本微信小程序在wxml保存文件不刷新页面的解决办法

将project.config.json中的skylineRenderEnable改为false "skylineRenderEnable": false

python爬虫-AES.CBS加密案例(mmz批量爬取)

下载mmz本页数据 批量下载请看主页&#xff01;&#xff01;&#xff01; 代码&#xff1a; import requests from Crypto.Cipher import AES import base64cookies {PHPSESSID: 48nu182kdlsmgfo2g7hl6eufsa,Hm_lvt_6cd598ca665714ffcd8aca3aafc5e0dc: 1710568549,SECKEY_A…

Redis学习笔记(基础篇)

Redis基础 1 Redis是什么&#xff1f;1.1 键值型1.2 NoSQL1.2.1 NoSQL与SQL的区别是什么1.2.2 总结 1.3 Redis的特点是什么&#xff1f; 2 Redis怎么用&#xff1f;2.1 Redis的基本命令2.2 Key的层级结构2.3 Redis的基本数据类型有哪些&#xff1f;2.1.1 String类型2.1.2 Hash类…