并发工具类-CountDownLatch
CountDownLatch 是 Java 中提供的一种非常有用的并发工具类,位于 java.util.concurrent 包中。它可以使一个或多个线程等待其他线程完成一组特定的操作后再继续执行。CountDownLatch 通过维护一个计数器来实现这一点,计数器的初始值由构造函数设定。每当一个线程完成了它的工作后,就调用 countDown() 方法将计数器减 1,当计数器减为 0 时,所有等待的线程都会被唤醒并继续执行。
1. CountDownLatch 的基本概念
1.1 核心思想
CountDownLatch 的核心思想是线程之间的协调与同步,它允许一个或多个线程等待一组事件发生后再执行某个操作。主要通过以下机制实现:
- 一个或多个线程调用
await()方法进入等待状态,直到计数器的值变为 0。 - 其他线程通过
countDown()方法递减计数器的值,每次递减操作代表一个任务完成。 - 当计数器的值变为 0 时,所有在
await()方法上等待的线程会被唤醒并继续执行。
1.2 工作流程
- 初始阶段:计数器值(
count)是通过构造方法设置的,表示有多少个任务需要完成。 - 任务完成:每个线程执行完一个任务后调用
countDown()方法,使计数器减 1。 - 等待线程:调用
await()的线程会阻塞,直到计数器的值变为 0,表示所有任务都已完成。
2. CountDownLatch 的构造方法和主要方法
2.1 构造方法
CountDownLatch 只有一个构造方法:
public CountDownLatch(int count)
count:传入的参数表示计数器的初始值,代表有多少个任务需要完成。
2.2 主要方法
-
await():
- 作用:调用该方法的线程会进入等待状态,直到计数器的值变为 0,或者线程被中断。
- 示例:
java latch.await(); -
countDown():
- 作用:将计数器的值减 1。每次调用表示一个任务完成,当计数器的值变为 0 时,所有调用了await()的线程会被唤醒。
- 示例:
java latch.countDown(); -
await(long timeout, TimeUnit unit):
- 作用:调用该方法的线程会等待指定的时间。如果计数器在指定时间内变为 0,线程继续执行;否则,线程会超时返回。
- 示例:
java latch.await(5, TimeUnit.SECONDS); -
getCount():
- 作用:返回当前计数器的值。
- 示例:
java long count = latch.getCount();
3. CountDownLatch 的使用场景
CountDownLatch 适合用于多线程协调场景,以下是一些典型的使用场景:
3.1 主线程等待多个子线程执行完毕
当主线程需要等待多个子线程执行完成后再继续执行时,可以使用 CountDownLatch 来实现。例如,在主线程中创建多个子线程去处理不同的任务,主线程调用 await() 方法等待所有子线程完成后再继续执行后续操作。
3.2 实现并行任务后合并结果
在分布式系统或高并发场景中,常常需要将一个任务拆分为多个子任务,并发执行后再合并结果。CountDownLatch 可以保证主线程等待所有并发子任务完成后,再对结果进行汇总。
3.3 模拟并发压力测试
在某些并发压力测试中,可能需要多个线程同时开始执行任务。CountDownLatch 可以用来控制多个线程同时开始某个操作,从而模拟高并发场景。
4. CountDownLatch 的实际使用示例
以下是一个使用 CountDownLatch 的典型示例,模拟主线程等待多个子线程完成任务。
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {// 创建 CountDownLatch,计数器初始值为3,表示有3个任务需要完成CountDownLatch latch = new CountDownLatch(3);// 启动3个子线程for (int i = 1; i <= 3; i++) {new Thread(new Worker(latch, "Worker-" + i)).start();}// 主线程等待,直到所有子线程执行完任务latch.await();System.out.println("All workers have finished. Main thread proceeding.");}
}class Worker implements Runnable {private CountDownLatch latch;private String name;public Worker(CountDownLatch latch, String name) {this.latch = latch;this.name = name;}@Overridepublic void run() {try {// 模拟工作过程System.out.println(name + " is working.");Thread.sleep((long) (Math.random() * 1000)); // 模拟工作时间System.out.println(name + " finished work.");} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {// 任务完成,调用 countDown() 方法,使计数器减1latch.countDown();}}
}
解释:
- 我们创建了一个
CountDownLatch对象,计数器初始值为 3。 - 创建了 3 个子线程(模拟 3 个工作者),每个线程执行完任务后调用
countDown(),将计数器减 1。 - 主线程调用
await()方法,等待所有子线程完成任务。 - 当计数器变为 0 时,主线程继续执行。
5. CountDownLatch 的应用场景举例
5.1 多服务启动协调
在微服务架构或分布式系统中,一个服务的启动可能依赖于其他服务。如果某个服务需要等待多个依赖服务启动完成后才能启动,可以使用 CountDownLatch 来协调启动流程。
public class ServiceStartupExample {private static final int NUM_OF_SERVICES = 3;private static CountDownLatch latch = new CountDownLatch(NUM_OF_SERVICES);public static void main(String[] args) throws InterruptedException {// 启动多个依赖服务new Thread(new Service("Service-1", 2000)).start();new Thread(new Service("Service-2", 3000)).start();new Thread(new Service("Service-3", 4000)).start();// 等待所有服务启动完毕latch.await();System.out.println("All dependent services are up. Starting main service.");}static class Service implements Runnable {private String serviceName;private int startupTime;public Service(String serviceName, int startupTime) {this.serviceName = serviceName;this.startupTime = startupTime;}@Overridepublic void run() {try {// 模拟服务启动时间System.out.println(serviceName + " is starting...");Thread.sleep(startupTime);System.out.println(serviceName + " is up.");} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {// 启动完成,计数器减1latch.countDown();}}}
}
在该示例中,主线程等待所有依赖服务启动完成后才继续启动主服务。CountDownLatch 用于协调各个依赖服务的启动过程,确保它们按预期的顺序和时间依赖性执行。
6. CountDownLatch 的线程安全与性能分析
6.1 线程安全
CountDownLatch 的内部实现是线程安全的,它通过内部的锁机制和原子操作来确保计数器的递减操作是安全的。每次调用 countDown() 时,计数器会以原子的方式递减,避免了竞态条件的出现。此外,await() 方法会保证线程阻塞直到计数器变为 0,从而确保多个线程可以同步进行操作。
6.2 性能
- 开销:
CountDownLatch的开销主要在于线程的阻塞与唤醒。对于少量线程的协调,这些开销通常是可以忽略的。然而,在高并发场景下,如果涉及大量线程调用await()进行阻塞等待,可能会导致一些性能开销。 - 使用场景:由于
CountDownLatch只能使用一次,计数器不能被重置,因此适用于一次性协调多个线程的场景。如果需要多个阶段的同步或复用,可以使用
CyclicBarrier 或其他并发工具。
7. CountDownLatch 与其他并发工具类的比较
- 与
CyclicBarrier:CyclicBarrier和CountDownLatch都用于协调多个线程的执行,但CyclicBarrier允许计数器重置,适用于循环使用的场景,而CountDownLatch是一次性使用的。 - 与
Semaphore:Semaphore用于控制对资源的并发访问,限制同时访问资源的线程数。而CountDownLatch则侧重于等待其他线程完成任务。
8. 总结
CountDownLatch 是一个非常有用的并发工具类,能够帮助多个线程之间协调执行顺序。它的工作原理基于计数器,线程可以通过 await() 方法等待,直到其他线程通过 countDown() 完成任务。CountDownLatch 在实际编程中的应用场景非常广泛,包括并发任务的协调、多线程测试中的并发压力模拟以及服务依赖启动等。
主要优点:
- 易于理解和使用,适用于需要一次性等待多个线程完成任务的场景。
- 线程安全,内部使用锁机制保证计数器的递减和线程阻塞的安全性。
缺点:
- 计数器不可重置,因此只能使用一次,如果需要重用,应该考虑使用
CyclicBarrier等工具类。
相关文章:
并发工具类-CountDownLatch
CountDownLatch 是 Java 中提供的一种非常有用的并发工具类,位于 java.util.concurrent 包中。它可以使一个或多个线程等待其他线程完成一组特定的操作后再继续执行。CountDownLatch 通过维护一个计数器来实现这一点,计数器的初始值由构造函数设定。每当…...
进程的重要函数
进程的重要函数: fork函数 了解fork函数 通过调用fork()函数,则会产生一个新的进程。调用fork()函数的进程叫做 父进程,产生的新进程则为子进程。 其编码过程: 1.函数功能: 函数头文件 #include <sys/types.h> #include <unistd.h> 函数…...
python 实现average median平均中位数算法
average median平均中位数算法介绍 平均(Mean)和中位数(Median)是统计学中常用的两个概念,用于描述一组数据的中心趋势,但它们并不是算法,而是数据处理的结果。不过,我可以解释如何…...
HTML概述
1. HTML概述 1.1 HTML定义 HTML超文本标记语言,其中超文本是链接,标记也叫标签(即带尖括号的文本)。 1.2 HTML基本骨架 HTML基本骨架是网页模板。 <html><head><title>网页的标题</title></head&…...
【FFT】信号处理——快速傅里叶变换【通俗易懂】
快速傅里叶变换(Fast Fourier Transform, FFT)是一种用于将信号从时间域转换到频率域的算法。 傅里叶变换的核心思想是:任何周期性信号都可以分解成多个不同频率的正弦波或余弦波的叠加。 简单来说,FFT可以帮助我们理解一个信号…...
电脑升级WIN11之后需要注意哪些东西
1.记事本,在前单位时,电脑升级后,记事本会需要手动更新,或手动安装 2.任务栏,WIN11默认任务栏在中间位置,想要调成WIN10一样的位置,分享两个方法 拖拽法(适用于Windows 11 2022年1…...
GEE 教程:利用sentinel-5p数据进行长时序CO一氧化碳的监测分析并结合夜间灯光数据分析
目录 简介 数据 哨兵5号 NOAA/VIIRS/DNB/MONTHLY_V1/VCMCF 函数 ui.Chart.image.series(imageCollection, region, reducer, scale, xProperty) Arguments: Returns: ui.Chart 代码 结果 简介 利用sentinel-5p数据进行长时序CO一氧化碳的监测分析并结合夜间灯光数据…...
【教程】鸿蒙ARKTS 打造数据驾驶舱---前序
鸿蒙ARKTS 打造数据驾驶舱 前面2章我介绍了如何通过定义View绘制箭头以及圆形进度,初步了解了鸿蒙如何进行自定义View。接下来我将通过我最近在带的一个VUE的项目,简单实现了几个鸿蒙原生页面。帮助大家快速上手纯血鸿蒙开发. 本项目基于Api11Stage模…...
Html css样式总结
1.Html css样式总结 CSS 定义 中文名称:层叠样式表 。 英文全称:Cascading Style Sheets ,简称CSS。在网页制作时采用CSS技术,可以有效地对页面的布局、字体、颜色、背景和其它效果实现更加精确的控制。 (1)…...
决策树基础概论
1. 概述 在机器学习领域,决策树(Decision Tree) 是一种高度直观且广泛应用的算法。它通过一系列简单的是/否问题,将复杂的决策过程分解为一棵树状结构,使得分类或回归问题的解决过程直观明了。决策树的最大特点在于可…...
Spring Boot集成Akka Cluster快速入门Demo
1.什么是Akka Cluster? Akka Cluster将多个JVM连接整合在一起,实现消息地址的透明化和统一化使用管理,集成一体化的消息驱动系统。最终目的是将一个大型程序分割成若干子程序,部署到很多JVM上去实现程序的分布式并行运算…...
django学习入门系列之第十点《A 案例: 员工管理系统10》
文章目录 12 管理员操作12.4 密码加密12.5 获取对象(防止id错误--编辑界面等)12.6 编辑管理员12.7 重置密码 往期回顾 12 管理员操作 12.4 密码加密 密码不应该以明文的方式直接存储到数据库,应该加密才放进去 定义一个md5的方法ÿ…...
Unity实战案例全解析:PVZ 植物卡片状态分析
Siki学院2023的PVZ免费了,学一下也坏 卡片状态 卡片可以有三种状态: 1.阳光足够,(且cd好了可以种植) 2.阳光不够,(cd?好了:没好 (三目运算符)&…...
判断变量是否为有限数字(非无穷大或NaN)math.isfinite() 判断变量是否为无穷大(正无穷大或负无穷大)math.isinf()
【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 判断变量是否为有限数字(非无穷大或NaN) math.isfinite() 判断变量是否为无穷大(正无穷大或负无穷大) math.isinf() 请问关于以下代码表述错误…...
idea使用阿里云服务器运行jar包
说明:因为我用的阿里云服务器不是自己的,所以一些具体的操作可能不太全面。看到一个很完整的教程,供参考。 0. 打包项目 这里使用的是maven打包。 在pom.xml中添加以下模块。 <build><plugins><plugin><groupId>org…...
解决nginx代理SSE接口的响应没有流式返回
目录 现象原来的nginx配置解决 现象 前后端分离的项目,前端访问被nginx反向代理的后端SSE接口,预期是流式返回,但经常是很久不响应,一响应全部结果一下子都返回了。查看后端项目的日志,响应其实是流式产生的。推测是n…...
11 - TCPClient实验
在上一个章节的UDP通信测试中,尽管通信的实现过程相对简洁,但出现了通信数据丢包的问题。因此,本章节将基于之前建立的WIFI网络连接,构建一个基础的TCPClient连接机制。我们利用网络调试助手工具来发送数据,测试网络通…...
React框架搭建,看这一篇就够了,看完你会感谢我
传统搭建框架的方式 在2024年以前,我们构建框架基本上采用官方脚手架,但是官方脚手架其实大概率都不符合我们的项目要求,搭建完了以后往往需要再继续集成一些第三方的包。这时候又会碰到一些版本冲突,配置教程等,往往…...
【rust】rust条件编译
在c语言中,条件编译是一个非常好用的功能,那么rust中如何实现条件编译呢? rust的条件编译需要两个部分,一个是fratures,另一个是cfg。Cargo feature是一个非常强大的功能,可以提供条件编译和可选依赖项的高级特性&…...
一键文本提示实现图像对象高质量剪切与透明背景生成
按照提示词裁剪 按照边框裁剪 要实现您描述的功能,即通过一个文本提示就能自动从图片中切割出指定的对象并生成一个带有透明背景的新图像,这需要一个结合了先进的计算机视觉技术和自然语言处理能力的系统。这样的系统可以理解输入的文本指令,并将其转化为对图像内容的精确分…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
