专注于 JetBrains IDEA 全家桶,永久激活,教程
提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,等工具的永久激活

Semaphore、CountDownLatch 的实现原理浅析

前言

搞懂这篇文章的前提是对 Lock 的几种实现以及 AQS 的源码原理有一定了解,如果不了解的话可以看下 Lock 中的 AQS、独占锁、重入锁、读锁、写锁、Condition 源码原理分析,本文源码未贴出来自己去翻下然后跟着图的调用逻辑走就能理清了

Semaphore

Semaphore(信号量)它通过 new Semaphore(permits) 来进行创建,permits 表示同一时间可以执行多少个线程。

使用 acquire 来获得许可,通过 release 来释放许可。在同一时间只允许 permits 个线程同时运行。

57_1.png

57_2.png

可以看到输出结果,当线程数量达到上限的时候,其它线程无法执行,释放了一个线程后归还一个信号量,那么下一个线程才能执行。

它的实现原理如下

57_3.png

1、 当调用 new Semaphore(5); 的时候就是说同一时刻只能有 5 个线程在同时执行。它的内部实现是创建了一个 NonfairSync,5 表示 state 的初始值,也就是信号量
2、 申请许可的时候,其实就是在调用 acquire()
3、 获取共享锁就是调用 NonfairSync 的 nonfairTryAcquireShared 方法
4、 获取一个信号量的话 tmp = state – 1,如果 tmp < 0 了表明”过载”了申请许可失败会将当前线程构造成 Waiter 结点放入同步队列中
5、 如果 tmp >= 0 的话,则调用 CAS 去更新值,在这个过程由于存在并发的情况失败了会无限循环调用 CAS 设置
6、 如果 CAS 设置成功,那么持有锁成功
7、 如果 CAS 设置锁失败(会不停的循环设置直到发现 state < 0 了还没有成功,如下图)会将当前线程构造成 Waiter 结点放入同步队列中
8、 同步队列中的线程处于等待状态,当调用 release 后或者发出线程中断操作后,这个线程又会开始从头结点开始争抢锁

57_4.png

CountDownLatch

CountDownLatch 内部维护了一个计数器,当计数器不为 0 的时候调用其 await() 可以进行阻塞,每次使用 countDown() 计数器值 – 1,当计数器值为 0 的时候,所有阻塞的线程从 await() 返回

利用这个特性我们可以用来合并多个线程最终的结果,或者以此来模拟并发请求调用等等,如下并发请求代码

此处的“锁”的成功与否表现其实就是 CountDownLatch 的一个同步值的变化

57_5.png

57_6.png

我们来分析下 CountDownLatch 是如何做到的以下两张图分别展示了在 countDown() 和 await() 的原理,源码跟着看就行了就不贴出来了

57_7.png

57_8.png

1、 countDown 的时候每次调用都会对 state 减 1 也就是我们 new CountDownLatch(10); 的这个计数器的数字减 1,如果为 0 的话说明该唤醒同步队列中的等待结点了,如果大于 0 的话这直接返回。

此时唤醒同步队列中的等待结点后,await() 中挂起的线程将被唤醒,然后再次将下一个结点设置为头结点,唤醒下一个结点如此往复,最终所有的线程将被唤醒

1、 此时后面调用了 await 方法它会去获取同步值发现为 0 的话成功返回,如果小于 0 的话,再次判断是否是头结点,如果是的话再次尝试获取同步值,如果发现 state 为 0 的话则将当前节点断开,设置同步队列中的下一个结点为头结点,调用释放锁逻辑。如果 state 不为 0 或者发现不是头结点就直接将线程挂起

文章永久链接:https://tech.souyunku.com/26066

未经允许不得转载:搜云库技术团队 » Semaphore、CountDownLatch 的实现原理浅析

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们