CyclicBarrier

简介: CyclicBarrier是让一组线程达到一个屏障(也叫做同步点),当这一组线程执行到达这个屏障(cyclicBarrier.await()代码处)时,这组线程才会继续往下执行。

1 CyclicBarrier

CyclicBarrier是让一组线程达到一个屏障(也叫做同步点),当这一组线程执行到达这个屏障(cyclicBarrier.await()代码处)时,这组线程才会继续往下执行。

CyclicBarrier比较适用于多线程计算的场景,当这些线程都执行到某一个预设地点以后,再执行另外的操作。例如,开多个线程批量处理数据,多所有数据都处理完成后再进行汇总分析的场景。

CyclicBarrier和之前介绍的CountDownLatch比较类似,他们的主要区别是:CountDownLatch计数器只能使用一次,而CyclicBarrier计数器可以通过reset方法重置。CyclicBarrier还提供了一些查看阻塞线程数量(getNumberWaiting),判断线程是否被阻塞(isBroken)的方法。

 

 

2  源码分析

1)  构造函数

函数parties参数指的是,等待多少个线程进入屏障点,即等待多少线程调用await方法,才算所有线程都达到屏障点。

函数barrierAction指的是当所有线程达到这个屏障以后,将执行此barrierAction的内容。

public CyclicBarrier(int parties) {
    this(parties, null);
}

 

public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}

 

2)  await

有两个版本,都是挂起当前线程,直到所有线程都达到屏障点时再继续执行。第二个版本支持让线程等待一定的时间,如果到时间以后还有线程没有达到屏障点,那么让已经达到屏障点的线程继续执行。

public int await() throws InterruptedException, BrokenBarrierException {
    try {
        return dowait(false, 0L);
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen
    }
}
public int await(long timeout, TimeUnit unit)
    throws InterruptedException,
           BrokenBarrierException,
           TimeoutException {
    return dowait(true, unit.toNanos(timeout));
}

 

3)  dowait

屏障的核心逻辑都是在此方法中实现的。从以下代码中我们可以看到,他的主要处理逻辑(异常或者其他非主要逻辑忽略)如下:

 - 首先检查是否所有线程都达到屏障状态了,如果是,那么执行构造函数第二个参数barrierAction执行的任务。

 - 如果不是所有线程都达到屏障状态,那么当前线程挂起。

 - 如果线程挂起时指定了挂起时间,那么当时间到以后,此线程被唤醒,接着唤醒此时已经达到屏障状态的线程。

private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException, TimeoutException {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        final Generation g = generation;
        if (g.broken)
            throw new BrokenBarrierException();
        if (Thread.interrupted()) {
            breakBarrier();
            throw new InterruptedException();
        }
        int index = --count;
        if (index == 0) {  // tripped
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand;
                if (command != null)
                    command.run();
                ranAction = true;
                nextGeneration();
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier();
            }
        }

        // loop until tripped, broken, interrupted, or timed out
        for (;;) {
            try {
                if (!timed)
                    trip.await();
                else if (nanos > 0L)
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
                if (g == generation && ! g.broken) {
                    breakBarrier();
                    throw ie;
                } else {
                    // We're about to finish waiting even if we had not
                    // been interrupted, so this interrupt is deemed to
                    // "belong" to subsequent execution.
                    Thread.currentThread().interrupt();
                }
            }

            if (g.broken)
                throw new BrokenBarrierException();

            if (g != generation)
                return index;

            if (timed && nanos <= 0L) {
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
        lock.unlock();
    }
}

 

 

3 示例

以下示例代码中,我们制定屏障数是3,所以当三个线程调用cyclicBarrier.await()时,所有挂起的线程将被唤醒,然后执行构造函数中指定的任务,当然被唤醒的线程也将继续执行。

public static void main(String[] args) {
    int num = 3;
    CyclicBarrier cyclicBarrier = new CyclicBarrier(num, ()->{
        System.out.println("
时间="+DateUtil.getCurrentTime()+" -回调线程- 工作线程到达屏障点后开始执行");
    });
    for (int i = 0; i < num; i++) {
        String name = "T-"+i;
        Thread thread = new Thread(() -> {
            try {
                doTask();
                System.out.println("时间="+DateUtil.getCurrentTime()+" -工作线程- thread="+name+" 完成任务,进入屏障点,等待其他线程");
                cyclicBarrier.await();
                System.out.println("时间="+DateUtil.getCurrentTime()+" -工作线程- thread="+name+" 所有线程已进入屏障点,继续执行");
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        thread.start();
    }
}
private static void doTask(){
    Random random = new Random();
    long time = random.nextInt(3000) ;
    try {
        Thread.sleep(time);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

 

运行结果如下,符合上面的分析。

时间=11:36:52:893 -工作线程- thread=T-0 完成任务,进入屏障点,等待其他线程

时间=11:36:53:174 -工作线程- thread=T-1 完成任务,进入屏障点,等待其他线程

时间=11:36:53:434 -工作线程- thread=T-2 完成任务,进入屏障点,等待其他线程

时间=11:36:53:434 -回调线程- 工作线程到达屏障点后开始执行

时间=11:36:53:434 -工作线程- thread=T-0 所有线程已进入屏障点,继续执行

时间=11:36:53:434 -工作线程- thread=T-1 所有线程已进入屏障点,继续执行

时间=11:36:53:434 -工作线程- thread=T-2 所有线程已进入屏障点,继续执行


 

 

 

相关文章
|
Prometheus 监控 Cloud Native
Prometheus VS InfluxDB
前言 除了传统的监控系统如 Nagios,Zabbix,Sensu 以外,基于时间序列数据库的监控系统随着微服务的兴起越来越受欢迎,比如 Prometheus,比如 InfluxDB。gtt 也尝试了一下这两个系统,希望能找到两者的差别,为以后选型提供一些帮助。
10410 0
|
关系型数据库 分布式数据库 数据库
学习分布式不得不会的ACP理论
2000年7月,加州大学伯克利分校的Eric Brewer教授在ACM PODC会议上提出CAP猜想。2年后,麻省理工学院的Seth Gilbert和Nancy Lynch从理论上证明了CAP。
3583 0
|
Serverless 对象存储 Python
函数计算实现 oss 上传超大 zip 压缩文件的自动解压处理
在本文中,以用户上传超大压缩文件( zip 类型)到 oss, oss 系统捕获 PutObjec/PostObject 的事件, 自动触发函数执行, 函数将压缩文件解压,并将对应的解压文件放在oss 指定的 bucket 的某个目录下为例,展示 FC 的灵丹妙手
10146 0
函数计算实现 oss 上传超大 zip 压缩文件的自动解压处理
|
大数据 数据挖掘 BI
Quick BI功能篇之(一):20分钟入门
最近小编帮助隔壁团队一个小姐姐解决了个大难题:给老板汇报业绩分析,频次提高、效率提升,还得保证团队中的小伙伴们都得有点大数据时代的基本数据能力。小编觉得这么好的经验可以分享给更多志同道合的朋友们,所以决定加班加点,推出Quick BI的功能、场景及技巧分享系列,为大数据时代舔砖加瓦!
5311 1
|
Java Linux 数据库连接
记阿里的一次壮烈牺牲
一、前言 今天本是一个阳光明媚,鸟语花香的日子。于是我决定在逛街中感受春日的阳光~结果晚上七点的时候,蚂蚁金服后端大佬来了电话,要进行一轮的技术面试。
1609 0
|
算法 Java 数据库
2018美团JAVA面试问题与总结
美团点评-美团 面经 首先是Java虚拟机垃圾回收 Synchronized与EntrantLock的区别。 数据库事务隔离 LRU最近事物 快排 大根堆 大部分都能答上,但是回答的不够深入 题目 Java虚拟机垃圾回收 ...
2175 1
|
Kubernetes Cloud Native Linux
Kubernetes不同发行版的比较
Kubernetes是目前最为成功和发展最快的IT基础架构项目之一。Kubernetes在2014年作为内部的Google orchestrator Borg的开源版本推出。在2017年各企业使用Kubernetes的情形有所增加,而到了2018年,从软件开发商到航空公司,它已经被广泛应用在各类业务上,Kubernetes之所以能够迅速普及发展的原因之一就在于其开源的架构,以及忠实的社区中所提供的大量帮助手册,各类文档以及技术支持等。
2930 0
|
算法 Java 程序员
阿里员工吐槽:阿里巴巴太累了,找到60万的工作我就离职
一名阿里员工因为加班顿生怨言:在阿里巴巴干着太累了,找到60万的工作我就离职,并且直言压力很大:心累,开发还得干产品的活,一天8个会,各种拉你,这边还得解决客户的问题,还得干PM的活推动各方开发,每到周日晚上就想辞职,有没有一样的?   看样子这名阿里员工已经不堪忍受这种工作状态了,其称自己目前的职级是P7,年收入差不多在70万左右,如果能找到60万的工作,虽然降薪了,但会毫不犹豫离职。
4453 0
|
存储 消息中间件 Java
史上最全、最详细的 kafka 学习笔记!
一、为什么需要消息系统 1.解耦: 允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。 2.冗余: 消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。
1780 0