并发——深入分析CountDownLatch的实现原理
1|0一、前言
最近在研究java.util.concurrent
包下的一些的常用类,之前写了AQS
、ReentrantLock
、ArrayBlockingQueue
以及LinkedBlockingQueue
的相关博客,今天这篇博客就来写一写并发包下的另一个常用类——CountDownLatch
。这里首先要说明一点,CountDownLatch
是基于AQS
实现的,AQS
才是真正实现了线程同步的组件,CountDownLatch
只是它的使用者,所以如果想要学习CountDownLatch,请一定先要弄懂AQS的实现原理。我以下的描述均建立在已经了解AQS
的基础之上。我之前写过一篇AQS
实现原理的分析博客,感兴趣可以看一看:并发——抽象队列同步器AQS的实现原理。
2|0二、正文
2|12.1 抽象队列同步器AQS
在说CountDownLatch
前,必须要先提一下AQS
。AQS
全称抽象队列同步器(AbstractQuenedSynchronizer),它是一个可以用来实现线程同步的基础框架。当然,它不是我们理解的Spring
这种框架,它是一个类,类名就是AbstractQuenedSynchronizer
,如果我们想要实现一个能够完成线程同步的锁或者类似的同步组件,就可以在使用AQS
来实现,因为它封装了线程同步的方式,我们在自己的类中使用它,就可以很方便的实现一个我们自己的锁。
AQS
的实现相对复杂,无法通过短短的几句话将其说清楚,我之前专门写过一篇分析AQS
实现原理的博客:并发——抽象队列同步器AQS的实现原理。
在阅读下面的内容前,请一定要先学习AQS的实现原理,因为CountDownLatch
的实现非常简单,完全就是依赖于AQS
的,所以我以下的描述均建立在已经理解AQS
的基础之上。可以阅读上面推荐博客,也可以自己去查阅相关资料。
2|22.2 CountDownLatch的实现原理
既然已经开始学习CountDownLatch
的实现原理了,那一定已经知道了它的作用,我这里就不详细展示了,简单介绍一下:CountDownLatch
的被称为门栓,可以将它看成是门上的锁,它会给门上多把锁,只有每一把锁都解开,才能通过。对于线程来说,CountDownLatch
会阻塞线程的运行,只有当CountDownLatc
内部记录的值减小为0
,线程才能继续向前执行。
CountDownLatch
底层通过AQS
实现,AQS
的一般使用方式就是以内部类的形式继承它,CountDownLatch
就是这么使用它的。在CountDownLatch
内部有一个内部类Sync
,继承自AQS
,并重写了AQS
加锁解锁的方法,并通过Sync
的对象,调用AQS
的方法,阻塞线程的运行。我们知道,创建一个CountDownLatch
对象时,需要传入一个整数值count
,只有当count
被减小为0
时线程才能通过await
方法,否则将被await
阻塞。这里实际上是这样的:当线程运行到await方法时,需要去获取锁(锁由AQS实现),若count不为0,则线程就会获取锁失败,被阻塞;若count为0,则就能顺利通过。CountDownLatch
是一次性的,因为没有方法可以增加count
的值,也就是说,一旦count
被减小为0
,则之后就一直是0
了,也就再也不能阻塞线程了。下面我们就从源码的角度来分析CountDownLatch
。
2|32.3 CountDownLatch的内部类
前面我们说过,CountDownLatch
内部定义了一个内部类Sync
,继承自AQS
,通过这个内部类来实现线程阻塞,下面我们就来看一看这个内部类的实现:
可以看到,内部类Sync的实现非常简单,它只实现了AQS
中的两个方法,即tryAcquireShared以及tryReleaseShared,这两个方法是AQS
提供的使用共享锁的接口。这也就表明,CountDownLatch
实际上是一种共享锁机制,即锁可以同时被多个线程获取,这个不难理解,因为一旦count
被减小为0,则所有线程通过await
方法时,都能够顺利通过,不会因为获取不到锁而阻塞。而且从上面的实现中我们可以看到,Sync
直接将count
值作为AQS
的state
的值,只有state
的值为0,线程才能获取锁,也就是获得执行权限。
2|42.4 CountDownLatch的成员变量和构造方法
下面来看一看CountDownLatch
的属性和构造方法:
2|52.5 await方法分析
CountDownLatch
类最最核心的两个方法就是await
以及ountDown
,我们先来看一看await
方法的实现:
await
的实现异常简单,只有短短一行代码,调用了AQS
中已经封装好的方法。这就是AQS
的好处,AQS
已经实现了线程的阻塞和唤醒机制,将实现的复杂性隐藏,而其他类只需要简单的使用它即可。为了方便理解,我们还是来看看acquireSharedInterruptibly
方法吧:
相信看到这里,对CountDownLatch
的实现原理已经有一个比较清晰的理解了。CountDownLatch
的实现完全就是依赖于AQS
的,所有再次提醒,如果以上内容理解不了,请先去学习AQS
。
2|62.6 countDown方法分析
下面我们来分析CountDownLatch
中另一个核心的方法——countDown
,
为了方便理解,我们还是来看一看AQS
中releaseShared
方法的实现:
3|0三、总结
如果直接去看CountDownLatch
的源码会发现,它的实现真的非常简单,包括注释在内,总共300
行代码,除去注释,连100
行代码都不到。因为它所作的工作,除了重写AQS
的两个方法外,其余的基本上就是调用AQS
提供的模板方法而已。所以,理解CountDownLatch
的过程,实际上是理解AQS
的过程,只要理解了AQS
,看懂CountDownLatch
的原理,不需要5
分钟。AQS
真的是Java
并发中非常重要的一个组件,很多类都是基于它实现的,比如还有ReentrantLock
,同时AQS
也是面试中的常考点,所以一定要好好研究。最后再次推荐我之前编写的有关AQS
的源码分析博客:并发——抽象队列同步器AQS的实现原理。
4|0四、参考
- JDK1.8源码
__EOF__

本文链接:https://www.cnblogs.com/tuyang1129/p/12692423.html
关于博主:在互联网洋流中垂死挣扎,但依旧乐观的Java小菜鸟一枚!
版权声明:转载博客请注明出处,并附上原文链接!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?