搞懂这篇文章的前提是对 Lock 的几种实现以及 AQS 的源码原理有一定了解,如果不了解的话可以看下 Lock 中的 AQS、独占锁、重入锁、读锁、写锁、Condition 源码原理分析 ,本文源码未贴出来自己去翻下然后跟着图的调用逻辑走就能理清了
Semaphore(信号量)它通过 new Semaphore(permits)
来进行创建,permits 表示同一时间可以执行多少个线程。
使用 acquire 来获得许可,通过 release 来释放许可。在同一时间只允许 permits 个线程同时运行。
可以看到输出结果,当线程数量达到上限的时候,其它线程无法执行,释放了一个线程后归还一个信号量,那么下一个线程才能执行。
它的实现原理如下
new Semaphore(5);
CountDownLatch 内部维护了一个计数器,当计数器不为 0 的时候调用其 await() 可以进行阻塞,每次使用 countDown() 计数器值 - 1,当计数器值为 0 的时候,所有阻塞的线程从 await() 返回
利用这个特性我们可以用来合并多个线程最终的结果,或者以此来模拟并发请求调用等等,如下并发请求代码
此处的“锁”的成功与否表现其实就是 CountDownLatch 的一个同步值的变化
我们来分析下 CountDownLatch 是如何做到的以下两张图分别展示了在 countDown() 和 await() 的原理,源码跟着看就行了就不贴出来了
new CountDownLatch(10);
的这个计数器的数字减 1,如果为 0 的话说明该唤醒同步队列中的等待结点了,如果大于 0 的话这直接返回。 此时唤醒同步队列中的等待结点后,await() 中挂起的线程将被唤醒,然后再次将下一个结点设置为头结点,唤醒下一个结点如此往复,最终所有的线程将被唤醒