小知识,大挑战!本文正在参与“ 程序员必备小知识 ”创作活动
本文同时参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金
Code皮皮虾 一个沙雕而又有趣的憨憨少年,和大多数小伙伴们一样喜欢听歌、游戏,当然除此之外还有写作的兴趣,emm…,日子还很长,让我们一起加油努力叭🌈
😁前言
博主 常年游荡于牛客面经区,总结了字节、阿里、百度、腾讯、美团等等大厂的高频考题,之后会逐步分享给大家,期待各位的关注、点赞!
✨CountdownLatch源码讲解
话不多说,开撸,哦不,开讲!!!
CountDownLatch主要有两个方法:==①:await(),②:countDown();==
那我就不先买个关子了,我直接说:
调用await()方法的线程会被阻塞,直到计数器 减到 0 的时候,才能继续往下执行;
countDown():将计数器减一,
相信小伙伴们看完了我的源码讲解之后就会明白了。
但是我们先从CountDownLatch构造函数看起
CountDownLatch构造函数
1 | java复制代码CountDownLatch countDownLatch = new CountDownLatch(2); |
这里可以看到CountDownLatch 的构造函数其实是new的一个Sync,且将我们传入的int类型值也作为了Sync的参数
1 | java复制代码public CountDownLatch(int count) { |
我们再点进来看,可见Sync继承了 AbstractQueuedSynchronizer 即AQS
而Sync的有参构造调用了setState(count);方法
再看看setState方法,==其实说白了就是将我们CountDownLatch传入的int值作为了AQS的同步状态。==
到这再强烈建议大家先入手AQS,入手了之后再看这并发包下的工具类真的不要太简单!!!
countDown()图示、源码讲解
再来看看 countDown 方法
1 | java复制代码public void countDown() { |
这里调用了sync的releaseShared方法,传入了arg = 1
而我们可以看到releaseShared涉及到了两个方法 tryReleaseShared(1)和doReleaseShared()
1 | java复制代码public final boolean releaseShared(int arg) { |
先看tryReleaseShared(1)
这源码相信给位小伙伴一看都能懂把,那我就简要总结下!
- 调用getState()方法获取同步状态,如果同步状态为0,则返回false,结合下面的代码看,意味着不能在减同步状态了
- 如果不为0,也可以说是大于0,那么用一个int变量记录将同步状态减一后的值
- 最后同步CAS设置同步状态为减一后的值,如果设置失败就自旋重试,如果成功就看减一后的值是不是0
1 | java复制代码protected boolean tryReleaseShared(int releases) { |
结合之前源码看,如果减一后的同步状态为0,那么就会调用 doReleaseShared()方法
1 | java复制代码private void doReleaseShared() { |
可能小伙伴们看了源码也没太懂,那我小小总结一下
- 当同步状态为0的时候才会去调用,doReleaseShared()方法
- 如果同步状态为0,说明锁没有线程占用
- 那么就涉及到doReleaseShared()方法,去看该线程后面有没有线程排队
- 如果有线程排队,那么upark将其唤醒,并且有没有节点都要更新当前节点的状态
await()图示、源码讲解
await()方法调用的 sync的acquireSharedInterruptibly(1)方法
1 | java复制代码public void await() throws InterruptedException { |
点进来看
第一个if没啥好说的,如果线程被打断了就要抛异常
主要是第二个if,涉及到tryAcquireShared(1),doAcquireSharedInterruptibly(1)方法
1 | java复制代码public final void acquireSharedInterruptibly(int arg) |
tryAcquireShared(1)跟传入的参数无关,就是看当前的同步状态是否为0,如果为0返回1,不为0返回-1
==那么有小伙伴问了,为什么要这么返回呢???==
不要着急,我们接着看第二个方法
1 | java复制代码protected int tryAcquireShared(int acquires) { |
doAcquireSharedInterruptibly
别的都可以不看,关键就在于这个for(;;)循环
我们可以看到,如果想要退出for循环,必须满足 ==(p == head) && (r >= 0)==,也就是==当前节点等于头节点,且同步状态为0==
tryAcquireShared我们上面介绍过
1 | java复制代码private void doAcquireSharedInterruptibly(int arg) |
ok,相信能看到这里的小伙伴可能差那么一点就能明白,那我再给各位小伙伴总结一下。
==创作不易,希望小伙伴们可以一键三连多多支持!!!== 😁
🔥总结
就像我开头说的,
调用await()方法的线程会被阻塞,直到计数器 减到 0 的时候,才能继续往下执行;
countDown():将计数器减一。
结合上面的源码讲解,==先说await()==
- 如果计数器不为0,那么tryAcquireShared返回的就一定为1,那么
r >= 0
就不会满足,那么就无法退出,会一直进行for循环即起到阻塞作用
再说countDown()
- 每调用一次countDown()方法就会去利用CAS将计数器减一
- 当同步状态为0的时候才会去调用,doReleaseShared()方法
+ **如果同步状态为0,说明锁没有线程占用**
- 那么就涉及到doReleaseShared()方法,去看该线程后面有没有线程排队
+ **如果有线程排队,那么upark将其唤醒,并且有没有节点都要更新当前节点的状态**
💖最后
我是 Code皮皮虾,一个热爱分享知识的 皮皮虾爱好者,未来的日子里会不断更新出对大家有益的博文,期待大家的关注!!!
创作不易,如果这篇博文对各位有帮助,希望各位小伙伴可以==一键三连哦!==,感谢支持,我们下次再见~
本文转载自: 掘金