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

news2024/12/29 10:09:20

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

🏷️系列专栏: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() {
    @Override
    public 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;
        }

        @Override
        public 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次,每次加1
                    for (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://www.coloradmin.cn/o/1524982.html

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

相关文章

QT UI窗口常见操作

MainWidget::MainWidget(QWidget *parent): QWidget(parent), ui(new Ui::MainWidget) {ui->setupUi(this);// 设置主窗口背景颜色QPalette plt;plt.setColor(QPalette::Window,QColor(180,220,130));this->setPalette(plt);// 禁止窗口最大化按钮setWindowFlags(windowF…

【前端Vue】Vue3+Pinia小兔鲜电商项目第1篇:认识Vue3,1. Vue3组合式API体验【附代码文档】

全套笔记资料代码移步&#xff1a; 前往gitee仓库查看 感兴趣的小伙伴可以自取哦&#xff0c;欢迎大家点赞转发~ 全套教程部分目录&#xff1a; 部分文件图片&#xff1a; 认识Vue3 1. Vue3组合式API体验 通过 Counter 案例 体验Vue3新引入的组合式API vue <script> ex…

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

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

Ubuntu18.04 中编译 TI 官方的ros驱动包中 autonomous_robotics_ros 包所存在的问题及解决方案

环境&#xff1a; 安装有 ROS 系统的 Ubuntu18.04 环境&#xff0c;并且已将 TI 官方的毫米波雷达 ROS 驱动下载到Ubuntu18.04系统中&#xff0c;如需获取此代码请点击此链接根据教程下载即可。 代码下载链接&#xff1a;TI IWR6843ISK ROS驱动程序搭建-CSDN博客 问题1&…

天软专业课 C语言 24

文章目录 基础知识进制转换字符在计算机内部的表示 程序设计的基本概念程序结构化程序设计 C程序设计的初识简单的C程序标识符、常量与变量整型数据实型数据字符型数据算数表达式赋值表达式自加自减与逗号运算符 顺序结构赋值语句数据的输出数据的输入复合语句与空语句程序实例…

Android VINF

周末搞这玩意欲仙欲死&#xff0c;没办法只有看看。VINTF是供应商接口对象&#xff08;VINTF 对象&#xff09;&#xff0c;准确的说&#xff0c;这个是属于兼容性矩阵概念。。。有点想起了以前看过的一个电影&#xff0c;异次元杀阵。。。下面是谷歌官方的图。 本质上其实就是…

Linux——开发工具yum与vim

Linux——开发工具yum与vim 文章目录 Linux——开发工具yum与vim一、Linux 软件包管理器-yum1.1 什么是软件包1.2 yum的使用 二、linux下的编辑器-vim2.1 vim的基本概念2.2 vim的基本操作插入模式下的基本命令底行模式下的基本指令 2.3 vim的配置 一、Linux 软件包管理器-yum …

C++面试宝典第35题:滑动窗口最大值

题目 给定一个数组nums,有一个大小为k的滑动窗口从数组的最左侧移动到数组的最右侧。滑动窗口每次只向右移动一位,你只可以看到在滑动窗口内的k个数字,请返回滑动窗口中的最大值。 示例: 输入:nums = [1, 3, -1, -3, 5, 3, 6, 7], k = 3 输出:[3, 3, 5, 5, 6, 7] 解析 这…

java数据结构与算法刷题-----LeetCode55. 跳跃游戏

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 解题思路&#xff1a;时间复杂度O( n n n)&#xff0c;空间复杂度…

人人开源ueditor富文本+SpringBoot后端,配置问题解决

一、序言 首先博主第一次开始去使用到人人开源的富文本&#xff0c;在使用时几个问题解决了一天&#xff0c;如果你也存在我想你可以往下认真看&#xff0c;因为这篇博客是我刚刚解决问题时马上就写的总结&#xff0c;首先在使用过程中得到的问题如下&#xff0c;根据这些问题然…

ChatGPT国内镜像站大全

#今天在知乎看到一个问题&#xff1a;“平民不参与内测的话没有账号还有机会使用ChatGPT吗&#xff1f;” 从去年GPT大火到现在&#xff0c;关于GPT的消息铺天盖地&#xff0c;真要有心想要去用&#xff0c;途径很多&#xff0c;别的不说&#xff0c;国内GPT的镜像站到处都是&…

HDFS EXERCISES

bash: hdfs: command not found...这可能是因为hdfs命令不在系统环境变量中 whereis hadoop 找到hadoop的位置 一旦找到Hadoop安装目录&#xff0c;您需要将其 bin 目录添加到PATH环境变量中。 vi ~/.bashrc .bashrc 是一个在Linux和Unix系统中用于Bash shell的配置文件。当…

数据结构——lesson8二叉树的实现

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

FFmpeg-aac、h264封装flv及时间转换

文章目录 时间概念流程api核心代码 时间概念 dts: 解码时间戳, 表示压缩帧的解码时间 pts: 显示时间戳, 表示将压缩帧解码后得到的原始帧的显示时间 时间基: time_base &#xff0c; 通常以ms为单位 时间戳: timestamp , 多少个时间基 真实时间&#xff1a;time_base * timest…

每日五道java面试题之mybatis篇(三)

目录&#xff1a; 第一题. MyBatis的框架架构设计是怎么样的?第二题. 为什么需要预编译?第三题. Mybatis都有哪些Executor执行器&#xff1f;它们之间的区别是什么&#xff1f;第四题. Mybatis中如何指定使用哪一种Executor执行器&#xff1f;第五题. Mybatis是否支持延迟加载…

DEiT中如何处理mask数据的?与MAE的不同

在DeiT里面&#xff0c;是通过mask的方式&#xff0c;将maskunmasked的patches输出进ViT中&#xff0c;但其实在下游任务输入的patches还是和训练时patches的数量N是一致的&#xff08;encoder所有的patches&#xff09;。 而MAE是在encoder中只encoder未被mask的patches 通过…

go和rust使用protobuf通信

先下载protoc 首先下载proc程序以生成代码 https://github.com/protocolbuffers/protobuf/releases 解压&#xff0c;然后把bin目录的位置放到环境变量 测试 rust作为server&#xff0c;rpc使用tonic框架 官方教程 go作为service&#xff0c;使用grpc go语言使用grpc 效…

课时67:流程控制_for循环_for基础

2.4.1 for基础 学习目标 这一节&#xff0c;我们从 基础知识、简单实践、小结 三个方面来学习。 基础知识 简介 生产工作中&#xff0c;我们有可能会遇到一种场景&#xff0c;需要重复性的执行相同的动作&#xff0c;我们在shell编程的过程中&#xff0c;我们可以借助于循环…

pyinstaller打包不显示DOS窗口

1 使用pyinstaller 打包 会不显示DOS窗口 打包的时候把-w去掉就行了

[数据集][目标检测]铝片表面工业缺陷检测数据集VOC+YOLO格式400张4类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;400 标注数量(xml文件个数)&#xff1a;400 标注数量(txt文件个数)&#xff1a;400 标注类别…