Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >简介且高效,从源码的角度带你理解CountDownLatch的设计原理。下次换你来拷打面试官!

简介且高效,从源码的角度带你理解CountDownLatch的设计原理。下次换你来拷打面试官!

作者头像
程序员牛肉
发布于 2025-03-31 11:18:32
发布于 2025-03-31 11:18:32
11500
代码可运行
举报
运行总次数:0
代码可运行

大家好,我是程序员牛肉。

今天来带大家一起从源码的角度学习一下JUC中的一个热门工具类:CountDownLatch。

首先我们要来介绍一下什么是CountDownLatch:

在多线程任务中,我们经常会遇到这样一种情况:我们需要先执行A任务和B任务。只有AB任务都执行完了才可以执行C任务。

比如说在微服务架构的订单体系中,当用户下单之后,我们需要先在库存中进行扣减,并且同时到营销服务中核对优惠劵。之后才会到运单服务中将订单转为运单。

那我们要如何实现两个任务相互进行等待,只有两个任务都执行成功之后才会进行下一步呢?

我说白了,这些东西没那么高端。我直接一个join过去就能实现这个功能。但是这样实在是太low了!有没有更加高效的方法呢?

有的有的兄弟,CountDownLatch就是用来解决着这种场景的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        CountDownLatch latch = new CountDownLatch(2);

        new Thread(() -> {
            System.out.println("库存服务开始执行...");
            try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
            System.out.println("库存服务执行完成");
            latch.countDown();
        }).start();

        new Thread(() -> {
            System.out.println("营销服务开始执行...");
            try { Thread.sleep(3000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
            System.out.println("营销服务执行完成");
            latch.countDown();
        }).start();

        try {
            latch.await();
            System.out.println("库存服务和营销服务都已完成,开始执行运单服务");
            executeShippingService();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    private static void executeShippingService() {
        System.out.println("运单服务开始执行...");
        try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        System.out.println("运单服务执行完成");
    }
}

其实CountDownLatch的原理没多高端。你可以理解为我们手动为维护了一个计数器。

在上述代码中,我们先创建了一个初始值为2的计数器

在执行前置的库存服务和营销服务的时候,每当这两个服务执行完的时候,就会对这个计数器做减一的操作。

当这个计数器为0的时候,我们就会进一步执行运单服务。

那在讲完了大致思想之后,我们就来深入的看一看源码部分吧。

我们可以看到,当我们使用CountDownLatch latch = new CountDownLatch(2)来创建计数器的时候,实际上这个数字被传递给了Sync这个类。

所以让我们追一下Sync这个类:

这一看就豁然开朗了,Sync继承了AQS这个线程同步抽象类。而CountDownLatch又是基于Sync去实现的。

当我们向CountDownLatch中传递一个变量的时候,其实就是基于Sync创建了一个共享变量state。

而CountDownLatch的countDown其实就是执行了syn包中的releaseShared方法:

因此我们来看一看这个方法的具体实现:

这一看太简单了,就是对我们之前构造的那个state参数进行减一的操作。那么唤醒主线程的await方法呢?

await方法中调用了syn的acquireSharedInterruptibly方法。

而AQS的这个方法是final类型修饰的,因此sync没有能力重写这个类,而是直接调用的AQS原生的方法:

首先:sync.acquireSharedInterruptibly方法传入的参数是1,意思是请求共享锁。

之后在acquireSharedInterruptibly方法中,先检查当前线程是否被中断,如果被中断了就直接抛异常。

之后调用了tryAcquireShared方法来判断之前设置的state的状态:

如果state为0的话(子任务已经全部执行完毕),就返回1,如果state不为0的话,就返回-1。

当state不为0的时候,说明子任务还没有执行完,就继续使用acquire方法将当前线程加入到阻塞队列中:

在acquire中,会不断的使用自旋重试的方法来检查state的值:

所以acquire的作用就是以自旋的方式来阻塞主线程,直到state为空之后,主线程才会从阻塞队列中弹出,继续往下执行。

所以整体看来CountDownLatch的源码还是比较简洁的,它就是维护了一个计数器。子线程在执行任务的时候,会对计数器进行减一的操作。而主线程会被AQS阻塞,不断的去重试这个计数器的值是否为0,只有计数器为0,主线程才会继续执行后续的方法

而由于CountDownLatch整体的设计思路中state要用来标识状态信息。因此要保证其是线程安全的。所以syn类并没有对外提供重置state变量的方法:

也就是说每一个CountDownLatch只能使用一次。每一次计数器为0之后,你都需要重新创建一个CountDownLatch。

因此其实当大家出现这种多线程编排的时候,其实我更加推荐大家使用Completablefuture的allof方法。可以看一看我之前写的这一篇文章学习一下这个类:

下次换你来拷打面试官!一文带你读懂企业常用异步编程核心工具类CompletableFuture

2025-01-29

今天的文章就聊到这里了,相信通过我的介绍,你已经大致了解了CountDownLatch的设计思路。希望我的文章可以帮到你。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-03-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员牛肉 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
CountDownLatch源码解析
CountDownLatch看着字面意思根本不知道这个类的大概作用是什么。。通过查阅相关资料,得出的结论是这个类能够让当前线程在其他线程都执行完毕之后再执行。那么它是怎么做到这点的呐?其实按照之前的AQS,通过判断队列中的节点个数是否为0也可以做哦!那么他是否采用这种方式?
写一点笔记
2020/08/25
2520
CountDownLatch源码解析
万字图解 Java 并发框架:Fork/Join、CountDownLatch、Semaphore、CyclicBarrier
在第一章节《1.6w 字图解 Java 并发:多线程挑战、线程状态和通信、死锁;AQS、ReentrantLock、Condition 使用和原理》,我们开启了 Java 高并发系列的学习,透彻理解 Java 并发编程基础,主要内容有:
码哥字节
2025/04/23
3380
万字图解 Java 并发框架:Fork/Join、CountDownLatch、Semaphore、CyclicBarrier
CountDownLatch 源码分析
CountDownLatch 源码分析 1. 在阅读源码时做了大量的注释,并且做了一些测试分析源码内的执行流程,由于博客篇幅有限,并且代码阅读起来没有 IDE 方便,所以在 github 上提供JDK1.8 的源码、详细的注释及测试用例。欢迎大家 star、fork ! 2. 由于个人水平有限,对源码的分析理解可能存在偏差或不透彻的地方还请大家在评论区指出,谢谢! 1. 基本介绍 Latch 这个单词的意思就是 “闭锁” ,这也是 jdk1.5 引入的一个并发组件,名字听上去并没有让我们对他的功能并没有
lwen
2018/04/17
6840
CountDownLatch 源码分析
Java多线程之---用 CountDownLatch 说明 AQS 的实现原理
前面的文章中说到了 volatile 以及用 volatile 来实现自旋锁,例如 java.util.concurrent.atomic 包下的工具类。但是 volatile 的使用场景毕竟有限,很多的情况下并不是适用,这个时候就需要 synchronized 或者各种锁实现了。今天就来说一下几种锁的实现原理。
古时的风筝
2018/07/31
8710
Java多线程之---用 CountDownLatch 说明 AQS 的实现原理
AQS、CountDownLatch、CyclicBarrier、Semaphore、Exchanger
在编程的竞技场中,多线程与并发是每一级大牛必备的技能!今天,就让我们深入了解Java并发武器库中的“五神兵”——AQS、CountDownLatch、CyclicBarrier、Semaphore、Exchanger的强大之处。他们如棋盘上的棋子,既能彼此协调,又能独当一面,解决了无数线程之问的冲突、同步与协作难题。
疯狂的KK
2024/06/04
1500
AQS、CountDownLatch、CyclicBarrier、Semaphore、Exchanger
Java之CountDownLatch原理浅析
调用countDown方法时,N就会减1,CountDownLatch的await方法会阻塞当前线程,直到N变成零。
闻说社
2024/11/13
1060
Java之CountDownLatch原理浅析
一文搞懂 CountDownLatch 用法和源码!
CountDownLatch 是多线程控制的一种工具,它被称为 门阀、 计数器或者 闭锁。这个工具经常用来用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。下面我们就来一起认识一下 CountDownLatch
cxuan
2021/01/06
1.4K0
Java Review - 并发编程_ 信号量Semaphore原理&源码剖析
Semaphore信号量也是Java中的一个同步器,与CountDownLatch和CycleBarrier不同的是,它内部的计数器是递增的,并且在一开始初始化Semaphore时可以指定一个初始值,但是并不需要知道需要同步的线程个数,而是在需要同步的地方调用acquire方法时指定需要同步的线程个数。
小小工匠
2021/12/30
3630
Java Review - 并发编程_ 信号量Semaphore原理&源码剖析
JDK源码分析-CountDownLatch
CountDownLatch 是并发包中的一个工具类,它的典型应用场景为:一个线程等待几个线程执行,待这几个线程结束后,该线程再继续执行。
WriteOnRead
2019/08/16
3250
JDK源码分析-CountDownLatch
CountDownLatch
CountDownLatch是一个同步工具类,它使得一个或多个线程一直等待,直至其他线程的操作执行完成后再接着执行。在Java并发中,CountDownLatch是一个常见的面试题。
黑洞代码
2021/01/14
4090
CountDownLatch
Java Review - 并发编程_ CountDownLatch原理&源码剖析
在日常开发中经常会遇到需要在主线程中开启多个线程去并行执行任务,并且主线程需要等待所有子线程执行完毕后再进行汇总的场景。
小小工匠
2021/12/30
2460
Java Review - 并发编程_ CountDownLatch原理&源码剖析
CountDownLatch、CyclicBarrier、Semaphore 的原理以及实例总结
在Java多线程编程中,有三种常见的同步工具类:CountDownLatch、CyclicBarrier、Semaphore。这些工具类使得我们可以在多个线程之间进行协调,实现更高效的并发处理。本文将对它们的原理和实例进行分析总结。
青山师
2023/05/05
2230
啰里吧嗦CountDownLatch
CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行
葫芦娃
2019/04/12
1.5K0
啰里吧嗦CountDownLatch
面试官:实战中用过CountDownLatch吗?详细说一说,我:啊这...
在很多的面经中都看到过提问 CountDownLatch 的问题,正好我们最近也在梳理学习AQS(抽象队列同步器),而CountDownLatch又是其中典型的代表,我们今天就继续来学一下这个同步工具类!
JavaBuild
2024/05/27
1550
面试官:实战中用过CountDownLatch吗?详细说一说,我:啊这...
JUC并发—7.AQS源码分析三
volatile、synchronized、CAS、AQS、读写锁、锁优化和锁故障、并发集合、线程池、同步组件
东阳马生架构
2025/04/27
410
CountDownLatch、CyclicBarrier和Semaphore区别及底层原理
   CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。   CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。
程序员云帆哥
2022/05/12
5380
面试官:说说CountDownLatch,CyclicBarrier,Semaphore的原理?
CountDownLatch适用于在多线程的场景需要等待所有子线程全部执行完毕之后再做操作的场景。
艾小仙
2021/01/05
5590
面试官:保证线程顺序执行的方法
join() 方法是 Thread 类的一部分,可以让一个线程等待另一个线程完成执行。当你在一个线程 T 上调用 T.join() 时,调用线程将进入等待状态,直到线程 T 完成(即终止)。因此,可以通过在每个线程启动后调用 join() 来实现顺序执行。
用户11397231
2025/01/24
720
面试官:保证线程顺序执行的方法
Java并发编程之CountDownLatch
在日常的开发中,可能会遇到这样的场景:开启多个子线程执行一些耗时任务,然后在主线程汇总,在子线程执行的过程中,主线程保持阻塞状态直到子线程完成任务。
布禾
2021/04/09
2810
我用这个同步工具类优化了一个人脸识别项目,启动时间从40分钟降到10分钟...
我将结合一个真实线上案例作为背景来展开讲解这一知识点。给大家讲清楚什么是同步工具类、适合的场景、解决了什么问题、各个实现方案的对比。希望对大家理解同步工具类这个知识点有所帮助。
陶朱公Boy
2022/10/28
2760
我用这个同步工具类优化了一个人脸识别项目,启动时间从40分钟降到10分钟...
推荐阅读
相关推荐
CountDownLatch源码解析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验