2021-05-29

Java多线程(10)

加法计数器CyclicBarrier

现实生活中我们经常会遇到这样的情景,在进行某个活动前需要等待人全部都齐了才开始。上课时要等全部的学生人都到齐了才上课,比赛时要等运动员都上场后才开始,只有集齐七龙珠才能召唤神龙。意思就是等到所有的线程都准备好了到齐之后再执行下一步的行动。下面的动图很好的描述了这个场景:

CyclicBarrier字面意思是“可重复使用的栅栏”,CyclicBarrier 相比 CountDownLatch 来说,要简单很多,其源码没有什么高深的地方,它是 ReentrantLock 和 Condition 的组合使用。

看如下示意图,CyclicBarrier 和 CountDownLatch 是不是很像,只是 CyclicBarrier 可以有不止一个栅栏,因为它的栅栏(Barrier)可以重复使用(Cyclic)。

cyclicbarrier-2

怎么使用CyclicBarrier

public CyclicBarrier(int parties)
public CyclicBarrier(int parties, Runnable barrierAction)

第一个参数是参与的线程数量,第二个是最后一个到来的线程应该执行的操作。

下面是CyclicBarrier的成员变量如下所示:

//同步操作锁
private final ReentrantLock lock = new ReentrantLock();
//线程拦截器
private final Condition trip = lock.newCondition();
//每次拦截的线程数
private final int parties;
//换代前执行的任务
private final Runnable barrierCommand;
//表示栅栏的当前代
private Generation generation = new Generation();
//计数器
private int count;
 
//静态内部类Generation
private static class Generation {
  boolean broken = false;
}

上面贴出了CyclicBarrier所有的成员变量,可以看到CyclicBarrier内部是通过条件队列trip来对线程进行阻塞的,并且其内部维护了两个int型的变量parties和count,parties表示每次拦截的线程数,该值在构造时进行赋值。count是内部计数器,它的初始值和parties相同,以后随着每次await方法的调用而减1,直到减为0就将所有线程唤醒。CyclicBarrier有一个静态内部类Generation,该类的对象代表栅栏的当前代,就像玩游戏时代表的本局游戏,利用它可以实现循环等待。barrierCommand表示换代前执行的任务,当count减为0时表示本局游戏结束,需要转到下一局。在转到下一局游戏之前会将所有阻塞的线程唤醒,在唤醒所有线程之前你可以通过指定barrierCommand来执行自己的任务。

用法举例

package SeamphoreAndCountDownLacth.SeamphoreSource.CyclicBarrierTest;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrier01 {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier=new CyclicBarrier(7, new Runnable() {
            @Override
            public void run() {
                System.out.println("所有人都来了,去吃饭吧");
            }
        });
        for(int i=1;i<=7;i++){
            int temp=i-1;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+":"+temp+"来了");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

应用场景

可以用于多线程计算数据,最后合并计算结果的场景。例如,用一个Excel保存了用户
所有银行流水,每个Sheet保存一个账户近一年的每笔银行流水,现在需要统计用户的日均
银行流水,先用多线程处理每个sheet里的银行流水,都执行完之后,得到每个sheet的日均银行流水,最后,再用barrierAction用这些线程的计算结果,计算出整个Excel的日均银行流水。

参考:https://blog.csdn.net/qq_39241239/article/details/87030142

https://www.jianshu.com/p/333fd8faa56e

https://zhuanlan.zhihu.com/p/265530418

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值