【JDK源码分析】通过源码分析CyclicBarrier

前言

CyclicBarrier它是什么?一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点。类似于朋友之间联系要在中午聚个会,几个朋友全部到齐后才开始喝酒吃菜。

源码

CyclicBarrier属性和构造器

public class 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;

    // 构造parties个参与者的栅栏
    public CyclicBarrier(int parties) {
        this(parties, null);
    }
    // 构造parties个参与者的栅栏,并且设置栅栏放开时要执行的任务
    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }
}    

CyclicBarrier方法

1. await方法

在所有参与者都已经在此 栅栏上调用 await 方法之前或者超时,会一直等待

    public public int int await() await() throws InterruptedException, BrokenBarrierException {
        throws InterruptedException, BrokenBar 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));
    }   

    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();
            }
            // 每调用一次await减少一次count数
            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)
                        // command执行出错
                        // 任务执行出错将栅栏状态设置成损坏、重置剩余等待线程数以及唤醒等待的线程
                        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();
        }
    }

breakBarrier方法调用的signalAll,以及dowait方法中trip.await、trip.awaitNanos在本人这篇【JDK源码分析】通过源码深入分析AbstractQueuedSynchronizer博客有分析,此处不再赘述。

   // 将栅栏状态设置成损坏、重置剩余等待线程数以及唤醒等待的线程
    private void breakBarrier() {
        generation.broken = true;
        count = parties;
        // 唤醒所有线程
        trip.signalAll();
    }
    // 重置栅栏,下次还可以再使用
    private void nextGeneration() {
        // 唤醒所有线程
        trip.signalAll();
        count = parties;
        generation = new Generation();
    }

2. reset方法

将栅栏重置为其初始状态并开启一个新的栅栏

    public void reset() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            breakBarrier();   // break the current generation
            nextGeneration(); // start a new generation
        } finally {
            lock.unlock();
        }
    }

CyclicBarrier的源码分析到此结束

用法

如下面,张锅和张嫂约起去火锅店吃火锅,二个人到齐后才叫老板上菜,下面上示例:

        final CyclicBarrier cyclicBarrier = new CyclicBarrier(2,
                () -> System.out.println(Thread.currentThread().getName() + "喊:老板,我们人到齐了,把菜上起!"));

        new Thread(() ->{

            System.out.println("张锅在去火锅店的路上...");
            try {
                Thread.sleep(System.currentTimeMillis() % 1000);
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }, "张锅").start();

        new Thread(() ->{
            System.out.println("张嫂在去火锅店的路上...");
            try {
                Thread.sleep(System.currentTimeMillis() % 1000);
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }, "张嫂").start();

打印结果

张锅在去火锅店的路上...
张嫂在去火锅店的路上...
张锅喊:老板,我们人到齐了,把菜上起!

总结

当遇到多条线程需要相互等待时,使用CyclicBarrier工具是一个比较好的选择。

原文地址:https://www.cnblogs.com/d-homme/p/9363485.html

时间: 2024-12-22 08:37:34

【JDK源码分析】通过源码分析CyclicBarrier的相关文章

通过源码分析MyBatis的缓存

看了通过源码分析MyBatis的缓存这篇文章后,自己跟着源码过了一遍,对mybatis的一级缓存和二级缓存有了更清楚的认识. 一级缓存是SqlSession级别的,同一个sqlSession在第二次执行一个相同参数的select语句并且第一次执行后没有对数据进行更新,就会直接从缓存取值,而不再进行查找.通过阅读源码了解了这个过程,首先org.apache.ibatis.session.defaults.DefaultSqlSession.select(String, Object, RowBou

通过源码了解ASP.NET MVC 几种Filter的执行过程

一.前言 之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神的工作,而且很多人觉得平时根本不需要知道这些,会用就行了.其实阅读源码是个很好的习惯,它不只停留在知道怎么用的阶段,而是让我们知道一系列的为什么,为什么这样设计,为什么这样使用....很多朋友应该看过<asp.net x 框架揭秘>这本书,确实不错,特别是边看源码边看书,可以有不小的收获.Ok,我不是大神,我只是心血来潮想看一下源码! 二.几种常见的Filter

MYSQL Study案例之--通过源码安装Mysql-5.6

MYSQL  Study案例之--通过源码安装Mysql-5.6 系统环境: 操作系统:RedHat EL6 DB Soft:  Mysql 5.6.4-m7 1.系统环境 [[email protected] Packages]# uname -a Linux rh6 2.6.32-358.el6.x86_64 #1 SMP Tue Jan 29 11:47:41 EST 2013 x86_64 x86_64 x86_64 GNU/Linux [[email protected] Packag

Ubuntu通过源码编译安装Octave 4.0

本教程/笔记,用于指导在Ubuntu及其他Linux系统上如何通过源码安装Octave. Octave简介 Octave是GNU旗下代替matlab的数学工具软件,语法与matlab高度兼容,并且支持一些独有的更清晰更符合Linux社区习惯的语法.虽然在一些具体工具包和部分特性和效率上不如matlab,但是对于一般用户,它是matlab的一个有效的合法的免费的替代工具. 3.8版本之前官方没有GUI界面,让想使用GUI,必须下载第三方的工具(如qtoctave). 3.8版本加入了实验性的GUI

Linux下通过源码编译GD库

因为之前都通过源码直接编译安装的lamp环境,所以好多扩展库都是没有安装的,突然现在要用到一个验证码类,imagecreate函数显示未定义,所以就来安装编译下GD库, 首先需要先安装 gd 前置库 : freetype ,jpegsrc,libpng. freetype wget "http://download.savannah.gnu.org/releases/freetype/freetype-2.4.0.tar.bz2" tar jxvf freetype-2.4.0.tar

Android 4.4 通过源码进行 root 操作

2019-05-28 关键字:rk root 笔者手里有一块运行着 Android4.4 操作系统的 rk3128 开发板.刚好还没 root 的,摸索了一方,找到一个可以成功 root 的方式,特此记录一下. 整个过程其实并没有网上说的这么复杂,就简单几步,照着做就好了. step 1 预置外部 su 程序进系统.这个 su 程序已经作为附件上传到网上了.下载链接如下 链接: https://pan.baidu.com/s/1dHBfJ-SipGx6rGbpuW-p6A 提取码: fda6 这

Linux下通过源码编译安装程序

ASK: Linux下通过源码编译安装程序(configure/make/make install的作用) configure Linux 平台有各种不同的配置,安装时需要通过 configure 来确定,如:编译器用的是 cc 还是 gcc.不同库文件所在目录等.执行 configure 后会生成 Makefile,Makefile 规定了用什么编译器.编译参数等信息. make 根据 Makefile 中规定的内容进行编译,生成的可执行文件放在当前目录或某个子目录. make install

【JDK源码分析】通过源码彻底理解ReentrantLock显示锁

前言ReentrantLock和synchronized一样是一个可重入的互斥锁,但ReentrantLock功能更强大,它提供了非公平和公平两种锁争用策略供使用者选择,而synchronized只有非公平一种.ReentrantLock提供了可中断的锁等待机制以及可用于多组线程需要分组唤醒的条件. 类图下面是ReentrantLock的类图,内部抽象类Sync继承了AbstractQueuedSynchronizer(以下简称AQS),公平锁FairSync.非公平锁NonfairSync继承

通过源码分析View的测量

要理解View的测量,首先要了解MeasureSpec,系统在测量view的宽高时,要先确定MeasureSpec. MeasureSpec(32为int值)由两部分组成: SpecMode(高2位):测量模式. SpecSize(低30位):某种测量模式下的规格大小. SpecMode有3类: UNSPECIFIED: 父容器不对view做大小限制,一般用于系统内部,表示一种测量状态. EXACTLY:精确模式.对应于:LayoutPrams中的match_parent和具体数值. AT_MO