CountDownLatch和CyclicBarrier模拟同时并发请求

有时候要测试一下某个功能的并发能力,又不要想借助于其他测试工具,索性就自己写简单的demo模拟一个并发请求就最方便了。如果熟悉jemter的测试某接口的并发能力其实更专业,此处只是自己折腾着玩。

CountDownLatch和CyclicBarrier是jdk concurrent包下非常有用的两个并发工具类,它们提供了一种控制并发流程的手段。其实查看源码它们都是在内部维护了一个计数器控制流程的

  • CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;
  • CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行。

CountDownLatch和CyclicBarrier的区别

  • CountDownLatch的计数器,线程完成一个记录一个,计数器是递减 计数器,只能使用一次
  • CyclicBarrier的计数器 更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行,计数器是递增 计数器提供reset功能,可以多次使用

另外Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

通常我们模拟并发请求,一般都是多开几个线程,发起请求就好了。但是方式,一般会存在启动的先后顺序了,算不得真正的同时并发!怎么样才能做到真正的同时并发呢?是本文想说的点,java中提供了闭锁 CountDownLatch, CyclicBarrier 刚好就用来做这种事就最合适了。

下面分别使用CountDownLatch和CyclicBarrier来模拟并发的请求

CountDownLatch模拟

 
package com.test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.concurrent.CountDownLatch; public class LatchTest { public static void main(String[] args) throws InterruptedException { Runnable taskTemp = new Runnable() {        // 注意,此处是非线程安全的,留坑 private int iCounter; @Override public void run() { for(int i = 0; i < 10; i++) { // 发起请求 // HttpClientOp.doGet("https://www.baidu.com/"); iCounter++; System.out.println(System.nanoTime() + " [" + Thread.currentThread().getName() + "] iCounter = " + iCounter); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }; LatchTest latchTest = new LatchTest(); latchTest.startTaskAllInOnce(5, taskTemp); } public long startTaskAllInOnce(int threadNums, final Runnable task) throws InterruptedException { final CountDownLatch startGate = new CountDownLatch(1); final CountDownLatch endGate = new CountDownLatch(threadNums); for(int i = 0; i < threadNums; i++) { Thread t = new Thread() { public void run() { try { // 使线程在此等待,当开始门打开时,一起涌入门中 startGate.await(); try { task.run(); } finally { // 将结束门减1,减到0时,就可以开启结束门了 endGate.countDown(); } } catch (InterruptedException ie) { ie.printStackTrace(); } } }; t.start(); } long startTime = System.nanoTime(); System.out.println(startTime + " [" + Thread.currentThread() + "] All thread is ready, concurrent going..."); // 因开启门只需一个开关,所以立马就开启开始门 startGate.countDown(); // 等等结束门开启 endGate.await(); long endTime = System.nanoTime(); System.out.println(endTime + " [" + Thread.currentThread() + "] All thread is completed."); return endTime - startTime; } } 

执行结果

 

CyclicBarrier模拟

 
// 与 闭锁 结构一致 public class LatchTest { public static void main(String[] args) throws InterruptedException { Runnable taskTemp = new Runnable() { private int iCounter; @Override public void run() { // 发起请求 // HttpClientOp.doGet("https://www.baidu.com/"); iCounter++; System.out.println(System.nanoTime() + " [" + Thread.currentThread().getName() + "] iCounter = " + iCounter); } }; LatchTest latchTest = new LatchTest(); // latchTest.startTaskAllInOnce(5, taskTemp); latchTest.startNThreadsByBarrier(5, taskTemp); } public void startNThreadsByBarrier(int threadNums, Runnable finishTask) throws InterruptedException { // 设置栅栏解除时的动作,比如初始化某些值 CyclicBarrier barrier = new CyclicBarrier(threadNums, finishTask); // 启动 n 个线程,与栅栏阀值一致,即当线程准备数达到要求时,栅栏刚好开启,从而达到统一控制效果 for (int i = 0; i < threadNums; i++) { Thread.sleep(100); new Thread(new CounterTask(barrier)).start(); } System.out.println(Thread.currentThread().getName() + " out over..."); } } class CounterTask implements Runnable { // 传入栅栏,一般考虑更优雅方式 private CyclicBarrier barrier; public CounterTask(final CyclicBarrier barrier) { this.barrier = barrier; } public void run() { System.out.println(Thread.currentThread().getName() + " - " + System.currentTimeMillis() + " is ready..."); try { // 设置栅栏,使在此等待,到达位置的线程达到要求即可开启大门 barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " - " + System.currentTimeMillis() + " started..."); } } 

执行结果

 

并发请求操作流程示意图如下:

 

此处设置了一道门,以保证所有线程可以同时生效。但是,此处的同时启动,也只是语言层面的东西,也并非绝对的同时并发。具体的调用还要依赖于CPU个数,线程数及操作系统的线程调度功能等,不过咱们也无需纠结于这些了,重点在于理解原理!

毕竟测试并发 还得用专业的工具 jmeter 还是很方便的.

欢迎工作一到八年的Java工程师朋友们加入Java高级交流群:854630135

本群提供免费的学习指导 架构资料 以及免费的解答

不懂得问题都可以在本群提出来 之后还会有直播平台和讲师直接交流噢

哦对了,喜欢就别忘了关注一下哦~
 

<think>好的,用户之前问过PhaserCyclicBarrier的区别,现在又想知道它们CountDownLatch的区别。我需要先回顾之前的内容,确保这次回答连贯。 用户可能是在学习Java并发工具,想搞清楚各个同步器的适用场景。他们可能对每个类的核心功能使用场景还有些混淆,特别是CountDownLatch其他两者的区别。 首先,CountDownLatchCyclicBarrier、Phaser的主要区别在于使用机制。CountDownLatch是一次性的,计数器不能重置,而CyclicBarrier可以循环使用,Phaser更灵活,支持动态调整线程数分阶段。 需要分点对比,明确每个类的特点,比如计数器是否可重置、线程角色、触发条件等。用户可能需要具体的例子来理解不同场景下的应用,比如CountDownLatch用于主线程等待多个子线程完成,而CyclicBarrier用于线程之间相互等待。 另外,要注意避免技术术语过多,用简单的中文解释,可能的话给出代码示例。同时,总结部分要简明,帮助用户快速决策何时使用哪个工具。 检查是否有遗漏的关键区别,比如CountDownLatch的计数器由主线程控制,而CyclicBarrier由子线程自己触发。还要提到Phaser的动态性,这是其他两个不具备的。 最后,确保结构清晰,使用对比表格,让用户一目了然。可能用户需要实际应用中的指导,比如何时选择哪个类,所以总结部分要强调各自的适用场景。</think>`CountDownLatch`、`CyclicBarrier` `Phaser` 都是 Java 并发编程中的同步工具类,但设计目标应用场景不同。以下是它们的核心区别: --- ### **1. CountDownLatch(倒计时锁存器)** #### **核心功能** - **目的**:允许一个或多个线程等待**其他线程完成操作**(等待一组事件发生)。 - **单向等待**:通过计数器机制,主线程(或其他线程)调用 `await()` 等待计数器归零,工作线程完成任务后调用 `countDown()` 减少计数器。 - **一次性**:计数器归零后不可重置(若需要重复使用,需重新创建实例)。 #### **使用场景** - **主线程等待子线程初始化完成**:例如启动服务时,主线程等待所有子模块初始化完毕。 - **并行任务完成后汇总结果**:多个线程处理任务,主线程等待所有结果返回。 #### **代码示例** ```java CountDownLatch latch = new CountDownLatch(3); // 初始化计数器为3 // 工作线程完成任务后减少计数器 new Thread(() -> { doWork(); latch.countDown(); // 计数器-1 }).start(); // 主线程等待计数器归零 latch.await(); System.out.println("所有任务已完成"); ``` --- ### **2. CyclicBarrier(循环屏障)** #### **核心功能** - **目的**:让一组线程**相互等待**,直到所有线程到达屏障后,再一起继续执行。 - **多线程协作**:所有线程都是对等的,需主动调用 `await()` 等待其他线程。 - **可重复使用**:所有线程到达屏障后,计数器自动重置,可继续用于下一轮同步。 #### **使用场景** - **分阶段并行任务**:例如多线程分别处理数据的不同部分,全部处理完后合并结果。 - **模拟并发请求**:多个线程同时就绪后发起请求。 #### **代码示例** ```java CyclicBarrier barrier = new CyclicBarrier(3, () -> { System.out.println("所有线程已就绪,开始执行下一阶段"); }); // 三个线程相互等待 for (int i = 0; i < 3; i++) { new Thread(() -> { doWork(); barrier.await(); // 等待其他线程 }).start(); } ``` --- ### **3. Phaser(阶段器)** #### **核心功能** - **目的**:比 `CyclicBarrier` 更灵活的同步器,支持**分阶段的多线程协作****动态调整参与线程数**。 - **动态性**:允许线程随时注册(`register()`)或注销(`arriveAndDeregister()`)。 - **多阶段控制**:自动推进到下一阶段(类似多个 `CyclicBarrier` 串联)。 #### **使用场景** - **动态任务分解**:例如游戏关卡中,每个关卡需要不同数量的线程协作。 - **分层任务**:多阶段流水线处理(如数据处理 → 验证 → 存储)。 --- ### **4. 三者的核心区别** | 特性 | CountDownLatch | CyclicBarrier | Phaser | |---------------------|-------------------------------|-----------------------------|----------------------------------| | **设计目标** | 等待事件完成 | 线程相互等待 | 动态分阶段的线程协作 | | **线程角色** | 主线程等待子线程 | 子线程相互等待 | 动态注册的子线程分阶段协作 | | **计数器重置** | 一次性(不可重置) | 自动重置(可循环使用) | 自动分阶段(无需手动重置) | | **动态调整线程数** | 不支持 | 不支持 | 支持(注册/注销) | | **触发条件** | 计数器归零 | 所有线程到达屏障 | 所有注册线程完成当前阶段 | | **触发动作** | 无 | 可定义屏障触发的回调任务 | 每个阶段结束后可自定义逻辑 | --- ### **5. 使用场景总结** - **CountDownLatch**: **主线程等待多个子线程完成初始化**(如服务启动时等待资源加载完成)。 - **CyclicBarrier**: **多个子线程需要相互等待后继续执行**(如并行计算的分阶段汇总结果)。 - **Phaser**: **动态线程数的多阶段协作**(如游戏关卡中,不同关卡需要不同数量的线程参与)。 --- ### **关键区别示例** - **CountDownLatch vs CyclicBarrier**: - `CountDownLatch` 的计数器由外部线程(如主线程)控制等待,而 `CyclicBarrier` 的计数器由子线程自身通过 `await()` 控制。 - `CountDownLatch` 是“主等子”,`CyclicBarrier` 是“子等子”。 - **CyclicBarrier vs Phaser**: - `CyclicBarrier` 的线程数固定,`Phaser` 的线程数可动态调整。 - `Phaser` 天然支持多阶段,而 `CyclicBarrier` 需手动重置才能进入下一阶段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值