CountDownLatch源码解读
使用场景
一句话总结:一个或多个线程,等待其他多个线程执行完毕后才继续执行。
具体场景:
- 某个线程等待其他多个线程执行完毕才能继续执行。就好比一个公交车,司机师傅需要等到所有人都上车后才能启动起步。
- 发令枪:就是多个线程等待某个线程执行完毕才能继续执行。
- 死锁检测:一个非常方便的使用场景是,你可以使用 n 个线程访问共享资源,在每次测试阶段的线程数目是不同的,并尝试产生死锁。(这个没有理解到)
实现原理
分析代码前,建议我们我们先自己根据对AQS的理解和对CountDownLatch的使用创建,
先尝试猜测一下其内部是如何实现的。
你的猜测
...
...
下面开始正式讲CDL的代码实现。
同样的,不用质疑CDL内部肯定使用了AQS(没看代码前这一点是可以分析得出的)
我们直接看CDL内部是如何利用AQS的。
Sync类
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
//1. 利用的是AQS的共享锁模式
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
CountDownLatch类
private final Sync sync;
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void countDown() {
sync.releaseShared(1);
}
总结
CDL利用的是AQS的共享锁,在实例化的时候,会把state设置为count的值。
await方法就是获得共享锁,但必须要等到state的值为0才能成功获得共享锁,否则进入同步队列等待
countDown方法就是在释放共享锁,释放逻辑就是对state的值减一,直到state的值为0才会成功释放
个人认为实现非常巧妙,在看代码前我推测出了肯定用了AQS的共享锁(这点是推测成功了),
但同时怀疑使用了AQS的条件队列(这点当时推测的时候不确定,因为不记得共享锁能不能使用条件队列了),
后来回顾了下AQS的条件队列,得知只有独占锁才能使用条件队列,于是又开始推测CDL的内部实现,可惜没能想出来😂
扩展
CountDownLatch 相常见面试题:
-
解释一下 CountDownLatch 概念?
-
CountDownLatch 和 CyclicBarrier 的不同之处?
系统推荐
- Spring框架源码关键点
- K8S 无感发布
- Spring RetryTemplate
- PGSQL GIN索引“失效”
- InnoDB存储引擎
- 批量修改git历史记录中的用户名和邮箱
- Javassist整理
- Lombok的Accessors导致EasyExcel读取失败
- SpringCloud总体认识
- Mermaid示例
- raft协议
- K8S常用命令
- 随机毒鸡汤:今天心情不好删几个微商,让他们知道,创业的道路并不是一帆风顺。