您现在的位置是:首页 > 编程 > 

【连载 10】CountDownLatch

2025-07-29 15:20:16
【连载 10】CountDownLatch 2.5 CountDownLatch前两个synchronized和ReentrantLock都是解决线程安全问题的好手,就像两把宝剑,可以披荆斩棘大杀四方。下面我们来探索java.util.concurrent包下面解决线程同步问题的功能类。在使用多线程进行性能测试的过程中,经常需要基于事件、时间点进行线程的同步。例如我们整点抢红包场景、前置数据并发初

【连载 10】CountDownLatch

2.5 CountDownLatch

前两个synchronizedReentrantLock都是解决线程安全问题的好手,就像两把宝剑,可以披荆斩棘大杀四方。下面我们来探索java.包下面解决线程同步问题的功能类。

在使用多线程进行性能测试的过程中,经常需要基于事件、时间点进行线程的同步。例如我们整点抢红包场景、前置数据并发初始化等。我们需要所有线程都到达某一个关键点之后,再进行下一步。在流程类的测试场景中,这种需求尤为常见。

要解决这类问题或者说实现此类需求,我们必然会用到线程同步类。CountDownLatch 是一个相对简单同步工具类,可以实现让一个或多个线程等待直到在其他线程中执行的操作完成后再继续执行。如果你觉得不太好理解,类似的概念就是JMeter中的集合点,其含义就是所有线程都到达集合点集合一下,然后再各走各路。CountDownLatch 适合用于一个或多个线程等待其他一组线程完成工作后再继续执行的场景。

CountDownLatch是通过线程安全的计数器数值判断是否到达集合点,工作流程如下:

(1)首先创建同步对象,并且设置同步数量 (2)任务线程执行任务,完成之后将计数器值减一 ()等待线程(通常是main线程)会阻塞(可以设置超时),直到计数器归零,继续执行后面代码

2.5.1 基础方法

CountDownLatch的构造方法如下:

代码语言:javascript代码运行次数:0运行复制

    public CountDownLatch(int count) {

        if (count < 0) throw new IllegalArgumentException("count < 0");

        this.sync = new Sync(count);

    }

这个方法只有一个int参数count,如果count小于0,会抛出异常。这个count就是需要同步的数量,对应CountDownLatch工作流的第1步。

CountDownLatch工作流第2步,用到的计数器减一的方法如下:

代码语言:javascript代码运行次数:0运行复制
    public void countDown() {

        sync.releaseShared(1);

    }

方法没有参数,直接调用即可,通常会跟try-catch-finally同时使用,以保障每一个任务线程都会执行countDown()方法。

CountDownLatch工作流第步,对应2个方法,方法一:

代码语言:javascript代码运行次数:0运行复制
    public void await() throws InterruptedException {

        sync.acquireSharedInterruptibly(1);

    }

方法二:

代码语言:javascript代码运行次数:0运行复制
    public boolean await(long timeout, TimeUnit unit)

        throws InterruptedException {

        return (1, (timeout));

    }

方法一可以看做方法二的无限等待版本。

当等待同步的线程执行到这个方法,会阻塞继续执行,直到CountDownLatch计数器归零或者等待超时。一般来说,不建议新手使用某个无线等待的阻塞方法,但在Java性能测试最佳实战中,笔者会推荐使用方法一。原因有两点:一是CountDownLatch通常是用在前置或者后置数据处理,并发执行时间无法准确估计;二是我们可以通过框架功能设计,规避CountDownLatch真发生无限等待异常场景,还可以更加灵活控制集合时间。

2.5.2 最佳实战

CountDownLatch方法较少,使用流程简单,通过下面这个例子,展示CountDownLatch使用最佳实战。

代码语言:javascript代码运行次数:0运行复制
package org.funtester.performance.section5;

import java..CountDownLatch;

/**

 * CountDownLatch示例

 */

public class CountDownLatchDemo {

    public static void main(String[] args) {

        CountDownLatch countDownLatch = new CountDownLatch();// 创建一个CountDownLatch实例

        for (int i = 0; i < ; i++) {// 创建个线程

            new Thread(() -> {// 创建一个线程

                try {

                    Thread.sleep(100);// 睡眠100毫秒

                } catch (InterruptedException e) {

                    throw new RuntimeException(e);

                } finally {

                    ();// 计数器减1

                }

                println(() + "  任务完成  " + ().getame());// 打印日志

            }).start();// 启动线程

        }

        println(() + "  等待任务完成  " + ().getame());// 打印日志

        try {

            countDownLatch.await();// 等待计数器归零

        } catch (InterruptedException e) {

            throw new RuntimeException(e);

        }

        println(() + "  等待结束  " + ().getame());// 打印日志

    }

}

在上面的示例中,首先创建了同步数量为的CountDownLatch对象,接着创建了个线程,每个线程下面100毫秒,然后计数器减一,打印任务完成日志。下面是控制台输出:

代码语言:javascript代码运行次数:0运行复制
1698497720691  等待任务完成  main

1698497720791  任务完成  Thread-0

1698497720791  任务完成  Thread-2

1698497720791  任务完成  Thread-1

1698497720791  等待结束  main

可以看到,main线程在到达await()方法后,阻塞了100毫秒后,个任务均完成,计数器归零,main线程执行了打印等待结束的代码。

2.5. 使用场景

CountDownLatch常用的使用场景如下:

  • 线程等待。使用CountDownLatch可以让一个或者多个线程一直等待,直到其他线程均完成预定的任务。例如:在并发初始化前置数据场景。
  • 发送起止信号。使用CountDownLatch功能可以给一组线程发送信号,启动或者结束改组线程。例如:整点抢红包场景。

在使用场景中,CountDownLatchjava.lang.Thread#sleep(long)方法有部分重合,有些场景甚至相互替代。这里笔者建议在多线程场景中尽量少使用java.lang.Thread#sleep(long)方法,相比之下,CountDownLatch有一下几点优势:

  • 更加精准控制线程同步时机。CountDownLatch 可以精确控制等待的线程数目,而 sleep 只能通过轮询来实现等待,容易出现错误。
  • 更好的性能。CountDownLatch 可以使线程处于等待状态而不是占用 CPU 时间片,sleep 会导致线程不停醒来循环等待。
  • 更优雅退出。CountDownLatch 通过 await() 设置超时控制退出等待,而sleep不行;CountDownLatch一旦计数器归零,程序会立即退出等待,而sleep必须等到sheep结束才行。
  • 使用更优雅。CountDownLatch的接口简单直接,而sleep需要估算时间,设置不当容易造成浪费。
  • 代码可读性强。CountDownLatch同步逻辑简单易懂,而sleep方法若无注释很难理解。

因此在需要等待其他线程完成的场景下,CountDownLatch是一个更加简单、可靠、安全的选择。

人无完人,类无全能。下面说一下CountDownLatch的缺点:

  • 无法复用。CountDownLatch对象创建好之后,就无法重置计数器,无法复用对象。
  • 不够灵活。CountDownLatch对象创建好之后,就无法增加计数器数值,只能调用方法进行减一,无法应对复杂多线程场景。

总而言之,CountDownLatch 主要应用于一次性的简单等待场景。对于复杂的多线程协调还需要其他更高级的同步工具。

书的名字:从 Java 开始做性能测试

如果本书内容对你有所帮助,希望各位不吝赞赏,让我可以贴补家用。赞赏两位数可以提前阅读未公开章节。我也会尝试制作本书的视频教程,包括必要的答疑。

本文参与 腾讯云自媒体同步曝光计划,分享自。原始发表:2025-01-09,如有侵权请联系 cloudcommunity@tencent 删除线程对象多线程日志同步

#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格

本文地址:http://www.dnpztj.cn/biancheng/1132630.html

相关标签:无
上传时间: 2025-07-18 20:59:21
留言与评论(共有 19 条评论)
本站网友 严妍
30分钟前 发表
设置不当容易造成浪费
本站网友 颈部保健操
0秒前 发表
然后再各走各路
本站网友 卫生技术资格考试
28分钟前 发表
而 sleep 只能通过轮询来实现等待
本站网友 雅礼中学
5分钟前 发表
CountDownLatch工作流第步
本站网友 冻干人
12分钟前 发表
发送起止信号
本站网友 咖啡瘦身
30分钟前 发表
代码语言:javascript代码运行次数:0运行复制package org.funtester.performance.section5; import java..CountDownLatch; /** * CountDownLatch示例 */ public class CountDownLatchDemo { public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch();// 创建一个CountDownLatch实例 for (int i = 0; i < ; i++) {// 创建个线程 new Thread(() -> {// 创建一个线程 try { Thread.sleep(100);// 睡眠100毫秒 } catch (InterruptedException e) { throw new RuntimeException(e); } finally { ();// 计数器减1 } println(() + " 任务完成 " + ().getame());// 打印日志 }).start();// 启动线程 } println(() + " 等待任务完成 " + ().getame());// 打印日志 try { countDownLatch.await();// 等待计数器归零 } catch (InterruptedException e) { throw new RuntimeException(e); } println(() + " 等待结束 " + ().getame());// 打印日志 } } 在上面的示例中
本站网友 大开眼界歌词
17分钟前 发表
要解决这类问题或者说实现此类需求
本站网友 上海盛大
22分钟前 发表
(timeout)); } 方法一可以看做方法二的无限等待版本
本站网友 钱袋子
5分钟前 发表
CountDownLatch工作流第步
本站网友 中药治疗湿疹
21分钟前 发表
一般来说
本站网友 神战权利之眼
24分钟前 发表
CountDownLatch工作流第步
本站网友 针扎
0秒前 发表
直到计数器归零
本站网友 河南省理工大学
22分钟前 发表
并发执行时间无法准确估计;二是我们可以通过框架功能设计
本站网友 如何改善皮肤粗糙
10分钟前 发表
以保障每一个任务线程都会执行countDown()方法
本站网友 任庆河
13分钟前 发表
CountDownLatch 适合用于一个或多个线程等待其他一组线程完成工作后再继续执行的场景
本站网友 韩国劳务
2分钟前 发表
sleep 会导致线程不停醒来循环等待
本站网友 我的财帮子
16分钟前 发表
然后再各走各路
本站网友 植眉
26分钟前 发表
可以实现让一个或多个线程等待直到在其他线程中执行的操作完成后再继续执行