CountDownLatch介绍和使用【Java多线程必备】

这篇具有很好参考价值的文章主要介绍了CountDownLatch介绍和使用【Java多线程必备】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

点击 Mr.绵羊的知识星球 解锁更多优质文章。

目录

一、介绍

二、特性

三、实现原理

四、适用场景

五、注意事项

六、实际应用


一、介绍

    CountDownLatch 是 Java 中的一个并发工具类,用于协调多个线程之间的同步。其作用是让某一个线程等待多个线程的操作完成之后再执行。它可以使一个或多个线程等待一组事件的发生,而其他的线程则可以触发这组事件。

二、特性

1. CountDownLatch 可以用于控制一个或多个线程等待多个任务完成后再执行。

2. CountDownLatch 的计数器只能够被减少,不能够被增加。

3. CountDownLatch 的计数器初始值为正整数,每次调用 countDown() 方法会将计数器减 1,计数器为 0 时,等待线程开始执行。

三、实现原理

    CountDownLatch 的实现原理比较简单,它主要依赖于 AQS(AbstractQueuedSynchronizer)框架来实现线程的同步。

    CountDownLatch 内部维护了一个计数器,该计数器初始值为 N,代表需要等待的线程数目,当一个线程完成了需要等待的任务后,就会调用 countDown() 方法将计数器减 1,当计数器的值为 0 时,等待的线程就会开始执行。

四、适用场景

1. 主线程等待多个子线程完成任务后再继续执行。例如:一个大型的任务需要被拆分成多个子任务并交由多个线程并行处理,等所有子任务都完成后再将处理结果进行合并。

2. 启动多个线程并发执行任务,等待所有线程执行完毕后进行结果汇总。例如:在一个并发请求量比较大的 Web 服务中,可以使用 CountDownLatch 控制多个线程同时处理请求,等待所有线程处理完毕后将结果进行汇总。

3. 线程 A 等待线程 B 执行完某个任务后再执行自己的任务。例如:在分布式系统中,一个节点需要等待其他节点的加入后才能执行某个任务,可以使用 CountDownLatch 控制节点的加入,等所有节点都加入完成后再执行任务。

4. 多个线程等待一个共享资源的初始化完成后再进行操作。例如:在某个资源初始化较慢的系统中,可以使用 CountDownLatch 控制多个线程等待共享资源初始化完成后再进行操作。

CountDownLatch 适用于多线程任务的协同处理场景,能够有效提升多线程任务的执行效率,同时也能够降低多线程任务的复杂度和出错率。

五、注意事项

1. CountDownLatch 对象的计数器只能减不能增,即一旦计数器为 0,就无法再重新设置为其他值,因此在使用时需要根据实际需要设置初始值。

2. CountDownLatch 的计数器是线程安全的,多个线程可以同时调用 countDown() 方法,而不会产生冲突。

3. 如果 CountDownLatch 的计数器已经为 0,再次调用 countDown() 方法也不会产生任何效果。

4. 如果在等待过程中,有线程发生异常或被中断,计数器的值可能不会减少到 0,因此在使用时需要根据实际情况进行异常处理。

5. CountDownLatch 可以与其他同步工具(如 Semaphore、CyclicBarrier)结合使用,实现更复杂的多线程同步。

六、实际应用

1. 案例一

    (1) 场景

    一个简单的 CountDownLatch 示例,演示了如何使用 CountDownLatch 实现多个线程的同步。

    (2) 代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * CountDownLatchCase1
 * 如何使用CountDownLatch实现多个线程的同步。
 *
 * @author wxy
 * @since 2023-04-18
 */
public class CountDownLatchCase1 {
    private static final Logger LOGGER = LoggerFactory.getLogger(CountDownLatchCase1.class);

    public static void main(String[] args) throws InterruptedException {
        // 创建 CountDownLatch 对象,需要等待 3 个线程完成任务
        CountDownLatch latch = new CountDownLatch(3);

        // 创建 3 个线程
        Worker worker1 = new Worker(latch, "worker1");
        Worker worker2 = new Worker(latch, "worker2");
        Worker worker3 = new Worker(latch, "worker3");

        // 启动 3 个线程
        worker1.start();
        worker2.start();
        worker3.start();

        // 等待 3 个线程完成任务
        latch.await();

        // 所有线程完成任务后,执行下面的代码
        LOGGER.info("All workers have finished their jobs!");
    }
}

class Worker extends Thread {
    private static final Logger LOGGER = LoggerFactory.getLogger(Worker.class);

    private final CountDownLatch latch;

    public String name;

    public Worker(CountDownLatch latch, String name) {
        this.latch = latch;
        this.name = name;
    }

    @Override
    public void run() {
        try {
            // 模拟任务耗时
            TimeUnit.MILLISECONDS.sleep(1000);
            LOGGER.info("{} has finished the job!", name);
        } catch (InterruptedException e) {
            LOGGER.error(e.getMessage(), e);
        } finally {
            // 一定要保证每个线程执行完毕或者异常后调用countDown()方法
            // 如果不调用会导致其他线程一直等待, 无法继续执行
            // 建议放在finally代码块中, 防止异常情况下未调用countDown()方法
            latch.countDown();
        }
    }
}

运行结果:

countdownlatch,Java,多线程必备,java

    在上面的代码中,首先创建了一个CountDownLatch对象,并指定需要等待的线程数为 3。然后创建了 3 个线程并启动。每个线程会模拟执行一个耗时的任务,执行完成后会调用 countDown() 方法将计数器减 1。在所有线程都完成任务后,主线程会执行 latch.await() 方法等待计数器为 0,然后输出所有线程都完成任务的提示信息。

    思考:如果不使用CountDownLatch情况将会是怎样呢?

    运行结果:

countdownlatch,Java,多线程必备,java

    由执行结果可知,主线程不会等待子线程结束后再执行。如果我们主线程(main) 需要其他线程执行后的结果,我们就需要使用countDownLantch让主线程和执行快的线程等待子线程全部执行完毕再向下执行。

    思考:如果某个线程漏调用.countDown();会怎么样呢?

    接下来我们模拟worker1线程异常,如果该线程异常latch.countDown()方法就无法被调用。

public void run() {
    try {
        // 模拟任务耗时
        if ("worker1".equals(name)) {
            throw new RuntimeException(name + "运行异常");
        }
        TimeUnit.MILLISECONDS.sleep(1000);
        LOGGER.info("{} has finished the job!", name);
        latch.countDown();
    } catch (InterruptedException e) {
        LOGGER.error(e.getMessage(), e);
    }
}

    运行结果:

countdownlatch,Java,多线程必备,java

    由运行结果可知,当worker1线程由于异常没有执行countDown()方法,最后state结果不为0,导致所有线程停在AQS中自旋(死循环)。所以程序无法结束。(如何解决这个问题呢?请看案例二)

countdownlatch,Java,多线程必备,java

2. 案例二

    (1) 场景

    当年刚工作不久,遇到一个这样的问题:远程调用某个api,大部分情况下需要2-3s才能读取到响应值。我需要解析响应的JSON用于后续的操作。由于这个调用是异步的,我没办法在主线程获取到响应的JSON值。

    当时第一时间想到的是让主线程休眠,但是休眠多久好呢?1、2、3s?显然是不行的,如果1s就请求成功并响应了,你要等3s,这不是浪费时间吗!

    于是,我就请教了公司一位大佬。他告诉我使用CountDownLatch。我恍然大悟,之前自己学过,但是一到战场上我就把他给忘记了(实践是检验真理的唯一标准)。

(2) 代码(偷个懒 哈哈 就是在案例一的代码中修改了await()方法)

    将latch.await()修改为 latch.await(5, TimeUnit.SECONDS),这段代码啥意思呢?就是当第一个线程到达await()方法开始计时,5s后不等待未执行完毕的线程,直接向下执行。这么写的好处是,当调用某个方法超时太久,不影响我们的主逻辑。(很实用)

// 等待 3 个线程完成任务
if (!latch.await(5, TimeUnit.SECONDS)) {
    LOGGER.warn("{} time out", worker1.name);
}

// 所有线程完成任务后,执行下面的代码
LOGGER.info("all workers have finished their jobs!");

    看一下加了latch.await(5, TimeUnit.SECONDS)方法后执行结果:

countdownlatch,Java,多线程必备,java文章来源地址https://www.toymoban.com/news/detail-743696.html

到了这里,关于CountDownLatch介绍和使用【Java多线程必备】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 巧用CountDownLatch实现多线程并行工作

    【前言】       CountDownLatch 是JDK提供的一个同步工具,它可以让一个或多个线程挂起等待,一直等到其他线程执行完成才会继续执行。常用方法有 countDown 方法和 await 方法, CountDownLatch 在初始化时,需要指定一个整数n作为计数器。当调用 countDown 方法时,计数器会被减1;

    2024年02月13日
    浏览(27)
  • 性能优化实战使用CountDownLatch

    1.分析问题 原程序是分页查询EventAffinityScoreDO表的数据,每次获取2000条在一个个遍历去更新EventAffinityScoreDO表的数据。但是这样耗时比较慢,测试过30万的数据需要2小时 单个线程一个个遍历去更新表数据太慢了,我想把2000的数据分成多份,每份200条,可以分成10份。每份用一

    2024年02月07日
    浏览(26)
  • ListenableFuture和countdownlatch使用example

    ListenableFuture可以允许你注册回调方法(callbacks),在运算(多线程执行)完成的时候进行调用, 或者在运算(多线程执行)完成后立即执行

    2024年02月07日
    浏览(36)
  • 5.5. Java并发工具类(如CountDownLatch、CyclicBarrier等)

    5.5.1 CountDownLatch CountDownLatch 是一个同步辅助类,它允许一个或多个线程等待,直到其他线程完成一组操作。 CountDownLatch 有一个计数器,当计数器减为0时,等待的线程将被唤醒。计数器只能减少,不能增加。 示例:使用CountDownLatch等待所有线程完成任务 假设我们有一个任务需

    2024年02月07日
    浏览(36)
  • CountDownLatch倒计时器源码解读与使用

    🏷️ 个人主页 :牵着猫散步的鼠鼠  🏷️ 系列专栏 :Java全栈-专栏 🏷️ 个人学习笔记,若有缺误,欢迎评论区指正   目录 1. 前言 2. CountDownLatch有什么用 3. CountDownLatch底层原理 3.1. countDown()方法 3.2. await()方法 4. CountDownLatch的基本使用 5. 总结 在很多的面经中都看到过提问

    2024年04月22日
    浏览(23)
  • keycloak~CountDownLatch在keycloak中的使用

    在Java中, CountDownLatch 是一个线程同步的辅助类,用于等待其他线程完成操作。如果 CountDownLatch 实例被丢失或无法访问,可能会导致无法正常使用该对象。这可能会导致等待线程永远处于等待状态,无法继续执行。 如果意外丢失了 CountDownLatch 对象,你可以尝试以下方法进行

    2024年02月09日
    浏览(34)
  • CountDownLatch用法详解

    深入理解CountDownLatch计数器 线程计数器 用于线程执行任务,计数 等待线程结束 用法一: 等待所有的事情都做完 始终是2个线程在做事情,等2个线程做完事情才会停止下来。 用法二:假设2个线程做事情,刚开始并行做事情,等一个执行完成之后,另一个才能执行( 实际还是计数 )

    2023年04月19日
    浏览(33)
  • CountDownLatch

    这个类是在java.util.concurrent并发包里面 允许一个或者多个线程一直等待,直到其他线程执行完成后再执行 举例:班长和5名同学都在教室里面写作业,班长必须等五个同学都走了之后,才能把教师门的锁锁上 这是通过一个计数器来实现的,计数器初始化的值为线程的数量,每

    2024年02月06日
    浏览(60)
  • CompletableFuture真香,可以替代CountDownLatch!

    之前我们提到了 Future 和 Promise。Future 相当于一个占位符,代表一个操作将来的结果。一般通过 get 可以直接阻塞得到结果,或者让它异步执行然后通过 callback 回调结果。 但如果回调中嵌入了回调呢?如果层次很深,就是回调地狱。 Java 中的 CompletableFuture 其实就是 Promise,用

    2024年02月08日
    浏览(30)
  • 19.详解AQS家族的成员:CountDownLatch

    关注王有志,一个分享硬核Java技术的互金摸鱼侠 欢迎你加入 Java人的提桶跑路群 :共同富裕的Java人 今天我们来聊一聊AQS家族中的另一个重要成员CountDownLatch。关于CountDownLatch的面试题并不多,除了问“是什么”和“如何实现的“外,CountDownLatch还会和CyclicBarrier进行对比: 什

    2024年02月08日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包