上一篇咱讲了 CountDownLatch 可以解决多个线程同步的问题,相比于 join 来说它的应用范围更广,不仅可以应用在线程上,还可以应用在线程池上。然而 CountDownLatch 却是一次性的计数器,以王者农药来说,咱们不可能一场团战就决定比赛的输赢,所以在某些场景下,咱们是需要重复使用某个等待功能的,这就是我们今天要介绍的另一个主角——CyclicBarrier。
创新互联主要从事网站设计、网站建设、网页设计、企业做网站、公司建网站等业务。立足成都服务洛浦,十年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:13518219792
CyclicBarrier 翻译为中文是循环(Cyclic)栅栏(Barrier)的意思,它的大概含义是实现一个可循环利用的屏障。
CyclicBarrier 作用是让一组线程相互等待,当达到一个共同点时,所有之前等待的线程再继续执行,且 CyclicBarrier 功能可重复使用。
比如磊哥要坐班车回老家,因为中途不允许上、下乘客,所以营运的公司为了收益最大化,就会等人满之后再发车。像这种等人坐满就发一班车的场景,就是 CyclicBarrier 所擅长的,因为它可以重复使用(不像 CountDownLatch 那样只能用一次)。
CountDownLatch:一个或者多个线程,等待另外 N 个线程完成某个事情之后才能执行。
CountDownLatch 就像玩王者农药开局的加载一样,所有人要等待其他人都加载 100% 之后才能开始游戏。
CyclicBrrier:N 个线程相互等待,直到有足够数量的线程都到达屏障点之后,之前等待的线程就可以继续执行了。
CyclicBrrier 就像老司机开车一样,如果车上还有空余的座位,那么所有人都得等着,直到座位被坐满之后,老司机才会发车。
- import java.util.Date;
- import java.util.Random;
- import java.util.concurrent.*;
- public class CyclicBarrierExample {
- public static void main(String[] args) {
- // 创建 CyclicBarrier
- final CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
- @Override
- public void run() {
- System.out.println("人满了,准备发车:" + new Date());
- }
- });
- // 线程调用的任务
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- // 生成随机数 1-3
- int randomNumber = new Random().nextInt(3) + 1;
- // 进入任务
- System.out.println(String.format("我是:%s 再走:%d 秒就到车站了,现在时间:%s",
- Thread.currentThread().getName(), randomNumber, new Date()));
- try {
- // 模拟执行
- TimeUnit.SECONDS.sleep(randomNumber);
- // 调用 CyclicBarrier
- cyclicBarrier.await();
- // 任务执行
- System.out.println(String.format("线程:%s 上车,时间:%s",
- Thread.currentThread().getName(), new Date()));
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (BrokenBarrierException e) {
- e.printStackTrace();
- }
- }
- };
- // 创建线程池
- ExecutorService threadPool = Executors.newFixedThreadPool(10);
- // 执行任务 1
- threadPool.submit(runnable);
- // 执行任务 2
- threadPool.submit(runnable);
- // 执行任务 3
- threadPool.submit(runnable);
- // 执行任务 4
- threadPool.submit(runnable);
- // 等待所有任务执行完终止线程池
- threadPool.shutdown();
- }
- }
以上代码执行结果如下:
从上述结果可以看出:当 CyclicBarrier 的计数器设置为 2 时,线程 2 和 线程 3 都到屏障点之后,老司机才会发第一波车,再 2s 之后,线程 1 和线程 4 也同时进入了屏障点,这时候老司机又可以再发一波车了。
我们先来看下 CyclicBarrier 的类图:
由上图可知 CyclicBarrier 是基于独占锁 ReentrantLock 实现的,其底层也是基于 AQS 的。
在 CyclicBarrier 类的内部有一个计数器 count,当 count 不为 0 时,每个线程在到达屏障点会先调用 await 方法将自己阻塞,此时计数器会减 1,直到计数器减为 0 的时候,所有因调用 await 方法而被阻塞的线程就会被唤醒继续执行。当 count 计数器变成 0 之后,就会进入下一轮阻塞,此时 parties(parties 是在 new CyclicBarrier(parties) 时设置的值)会将它的值赋值给 count 从而实现复用。
CyclicBarrier(parties):初始化相互等待的线程数量的构造方法。
CyclicBarrier(parties,Runnable barrierAction):初始化相互等待的线程数量以及屏障线程的构造方法,当 CyclicBarrier 的计数器变为 0 时,会执行 barrierAction 构造方法。
getParties():获取 CyclicBarrier 打开屏障的线程数量,也称为方数。
getNumberWaiting():获取正在CyclicBarrier上等待的线程数量。
await():在 CyclicBarrier 上进行阻塞等待,直到发生以下情形之一:在 CyclicBarrier 上等待的线程数量达到 parties,则所有线程被释放,继续执行;
await(timeout,TimeUnit):在CyclicBarrier上进行限时的阻塞等待,直到发生以下情形之一:
isBroken():获取是否破损标志位 broken 的值,此值有以下几种情况:
reset():使得CyclicBarrier回归初始状态,直观来看它做了两件事:
CyclicBrrier 是通过独占锁 ReentrantLock 实现计数器的原子性更新的,CyclicBrrier 最常用的是 await() 方法,使用此方法会将计数器 -1,并判断当前的计数器是否为 0,如果不为 0 就会阻塞等待,并计时器为 0 之后,才能继续执行剩余任务。CyclicBrrier 相比于 CountDownLatch 来说,它的优势在于可以重复使用。
文章题目:CyclicBarrier:人齐了,老司机就发车了!
链接URL:http://www.shufengxianlan.com/qtweb/news49/179749.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联