对于并发执行,Java中的CountDownLatch是一个重要的类。为了更好的理解CountDownLatch这个类,本文将通过例子和源码带领大家深入解析这个类的原理,感兴趣的可以学习一下
前言
对于并发执行,Java中的CountDownLatch
是一个重要的类,简单理解, CountDownLatch
中count down
是倒数的意思,latch则是“门闩”的含义。在数量倒数到0的时候,打开“门闩”, 一起走,否则都等待在“门闩”的地方。
为了更好的理解CountDownLatch
这个类,本文通过例子和源码带领大家深入解析这个类的原理。
介绍和使用
例子
我们先通过一个例子快速理解下CountDownLatch
的妙处。
最近LOL S12赛如火如荼举行,比如我们玩王者荣耀的时候,10个万玩家登入游戏,每个玩家的网速可能不一样,只有每个人进度条走完,才会一起来到王者峡谷,网速快的要等网速慢的。我们通过例子模拟下这个过程。
运行结果:
概述
CountDownLatch
一般用作多线程倒计时计数器,强制它们等待其他一组(CountDownLatch
的初始化决定)任务执行完成。
构造器:
public CountDownLatch(int count)
:设置倒数器需要倒数的数量
常用API:
public void await() throws InterruptedException
:调用await()方法的线程会被挂起,等待直到count值为0再继续执行。public boolean await(long timeout, TimeUnit unit) throws InterruptedException
:同await(),若等待timeout时长后,count值还是没有变为0,不再等待,继续执行。时间单位如下常用的毫秒、天、小时、微秒、分钟、纳秒、秒。public void countDown()
: count值递减1public long getCount()
:获取当前count值
常见使用场景:
一个程序中有N个任务在执行,我们可以创建值为N的CountDownLatch,当每个任务完成后,调用一下countDown()方法进行递减count值,再在主线程中使用await()方法等待任务执行完成,主线程继续执行。
实现思路
通过前面的例子和介绍我们知道CountDownLatch的大致使用流程:
- 创建
CountDownLatch
并设置计数器值。 - 启动多线程并且调用
CountDownLatch
实例的countDown()
方法。 - 主线程调用
await()
方法,这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务,count值为0,停止阻塞,主线程继续执行。
不妨我们先思考下,它是怎么实现的呢?我们可以问自己几个问题?
- 如何做到可以让主线程阻塞等待在那里?是不是可以调用
LockSupport.park()
方法进行阻塞。 - 那么什么时候该阻塞呢?我们需要有个变量,比如state, 如果state大于0,就阻塞主线程。
- 那么什么时候该唤醒呢,又如何唤醒呢?如果任务执行完成后,我们让state 减去1,也就是调用
countDown()
方法,如果发现state是0,那么就调用LockSupport.unpark()
唤醒此前阻塞的地方,继续执行。
是不是很熟悉,这就是我们的AQS共享模式的实现原理啊,不了解AQS共享模式的可以参考本篇文章:深入浅出理解Java并发AQS的共享锁模式
我们把思路理清楚后,直接看CountDownLatch
的源码。
源码解析
类结构图
以上是CountDownLatch
的类结构图,
Sync
是CountDownLatch
的内部类,被成员变量sync
持有。Sync
继承了AbstractQueuedSynchronizer
,也就是我们大名鼎鼎的AQS。
await() 实现原理
1.线程调用 await()
会阻塞等待其他线程完成任务
2.doAcquireSharedInterruptibly()
方法是实现线程阻塞的核心逻辑
3.parkAndCheckInterrupt()
方法中会进行阻塞操作
countDown()实现原理
1.任务结束调用 countDown()
完成计数器减一(释放锁)的操作
2.调用tryReleaseShared()
方法尝试释放锁,true表示state等于0,去唤醒阻塞线程。
3.调用doReleaseShared()
唤醒阻塞的节点
以上就是Java CountDownLatch的源码硬核解析的详细内容,更多关于Java CountDownLatch的资料请关注编程学习网其它相关文章!