当前位置: 代码迷 >> 综合 >> CountDownLatch、CyclicBarrier 和 Semaphore原理分析
  详细解决方案

CountDownLatch、CyclicBarrier 和 Semaphore原理分析

热度:45   发布时间:2024-01-04 21:37:33.0

说明:本篇文章是在阅读《Java 并发编程艺术》过程中的一些笔记和分析,由于本人能力有限,如果有书写错误的地方,欢迎各位大佬批评指正!我们互相交流,学习,共同进步!

该项目的地址:https://github.com/xiaoheng1/concurrent-programming

欢迎有兴趣的小伙伴加入,一起讨论、分析,共同进步!

Java 中常用的并发工具有 CountDownLatch、CyclicBarrier 和 Semaphore.

1.CountDownLatch 等待所线程完成.

比如说,我将一个计算任务拆分成多个任务,然后多个线程分别计算,最后等所有任务计算完成后,在继续执行.

其实还有一个思路可以实现该功能,join. 但是 join 方法是有局限的,join 方法用于当前执行线程等待 join 线程执行结束,其实现原理是
不停的检查 join 线程是否存货,如果 join 线程存货,则让当前线程永远等待.

现在来看下 CountDownLatch 的实现原理.

CountDownLatch 的实现思路很巧妙,一开始就将 state 设置为非 0(代表该线程被阻塞),每次调用 countDown 释放锁,当最终 state = 0
时,说明可以申请到锁了,则主线程执行.

如果没有申请到锁怎么处理了?该线程可能先自旋一会,最终调用 LockSupport.park 方法自我阻塞.

2.CyclicBarrier 同步屏障

CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到所有线程都到达某个公共屏障点(也可以叫同步点),
即相互等待的线程都完成调用await方法,所有被屏障拦截的线程才会继续运行await方法后面的程序。
在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时CyclicBarrier很有用。
因为该屏障点在释放等待线程后可以重用,所以称它为循环的屏障点。CyclicBarrier支持一个可选的Runnable命令,
在一组线程中的最后一个线程到达屏障点之后(但在释放所有线程之前),该命令只在所有线程到达屏障点之后运行一次,
并且该命令由最后一个进入屏障点的线程执行.

3.CyclicBarrier 的应用场景

CyclicBarrier 可以用于多线程计算数据,最终合并计算结果的场景.

原理:CyclicBarrier 有栅栏的意思,比如说有三个线程执行,线程A执行较快,它执行完了,要等线程B和线程C. 等这三个线程都执行完后,这个
栅栏才打开放行,这三个线程继续执行,如何实现的了?

每个线程执行 await 方法,count–. 然后使用 condition await 等待. 等 count 为 0 时,开始放行. 设计的很精妙.

4.CountDownLatch 和 CyclicBarrier 的区别

CountDownLatch 只能使用一次,而 CyclicBarrier 可以使用多次.

CountDownLatch 和 CyclicBarrier 的语义不好区分,但是仔细品味下还是能发现一些区别的. CountDownLatch 是说其他线程就绪后,主线程
开始执行,而 CyclicBarrier 比如说公司团建,现在在大巴车那等,先到得人要等后到的人到齐后,大巴车才发车去目的地(Runnable),然后公司员工可以干
其他活,例如乒乓球比赛等.

5.Semaphore
Semaphore 是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保障合理的使用公共资源.

实现:
Semaphore 是如何做到对某一物理或逻辑资源访问数量的限制了?

答案是 AQS.

比如我定义:Semaphore(10), 同一时刻只能有 10 个线程访问线程池,每来一个线程,state -1,当第11线程访问的结果是啥了?
由于 state < 0 而被阻塞.

当一个线程访问完后,调用 release 方法,state + 1. 然后唤醒等待访问线程池的线程.

注意:

由于 release 方法中,没有对 state < 0 进行判断,每调用一次 release 方法 state + 1.
也就是说:虽然我定义的 state = 10. 但是我调用 11 次 release 方法,那么此时 state = 11.这就完成了
Semaphore 的动态增长.
如果说我觉得10个线程同时访问线程池,线程池扛不住了,我要减少同时访问的数量,该怎么办了?继承 Semaphore 类,
重写 reducePermits 方法就好了.

Semaphore 和 CountDownLatch 对比分析

CountDownLatch 是为了共同干一件事,线程1完成A部分,线程2完成B部分,线程3完成C部分. 而 Semaphore 是为了限制同一时间对
某一资源访问的数量, 例如:同一时间只允许10个线程同时访问.

6.线程间交换数据 Exchanger

Exchanger 提供了一个同步点,两个线程可以交换彼此的数据,如果第一个线程先执行 exchange() 方法,它会一直等待第二个线程也执行
exchange() 方法,当两个线程都达到同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方.

  相关解决方案