欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CountDownLatch、CyclicBarrier 和 Semaphore原理分析

程序员文章站 2022-04-20 14:57:29
...
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() 方法,当两个线程都达到同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方.