【同步工具类:CyclicBarrier】

news2024/11/15 21:58:23

同步工具类:CyclicBarrier

  • 介绍
  • 源码分析
    • CyclicBarrier 基于ReetrantLock + Condition实现。
    • 构造函数
    • await() 函数
  • 业务场景
    • 方案一:
      • 代码实现
      • 测试截图
    • 方案二
      • 代码实现
      • 测试打印
  • 总结

介绍

官方介绍:
一种同步辅助工具,允许一组线程都等待对方到达共同的障碍点。CyclicBarrier在涉及固定大小的线程组的程序中非常有用,这些线程组偶尔必须彼此等待。该屏障被称为循环屏障,因为它可以在释放等待线程后重新使用。
CyclicBarrier支持可选的Runnable命令,该命令在参与方中的最后一个线程到达后,但在释放任何线程之前,在每个障碍点运行一次。此屏障动作对于在任何一方继续之前更新共享状态都很有用。
通俗理解:
它可以协同多个线程,让多个线程在这个栅栏前等待,直到所有线程都达到了这个栅栏时,再一起继续执行后面的动作.
举个例子,你和朋友约定在公交站汇合,去公园玩。这个公交站相当于栅栏。只有你们都到了公交站,才一起去公园。

源码分析

CyclicBarrier 基于ReetrantLock + Condition实现。

    /** The lock for guarding barrier entry */
    //用于线程之间互相唤醒
    private final ReentrantLock lock = new ReentrantLock();
    /** Condition to wait on until tripped */
    private final Condition trip = lock.newCondition();
    //总线程数
    private final int parties;

构造函数

可以看到,不仅可以传入 参与方的总数量(即 parties)。还可以传入一个回调函数,当所有的线程被唤醒时,barrierAction 被执行,该参数可以为空。

    /**
     * Creates a new {@code CyclicBarrier} that will trip when the
     * given number of parties (threads) are waiting upon it, and which
     * will execute the given barrier action when the barrier is tripped,
     * performed by the last thread entering the barrier.
     *
     * @param parties the number of threads that must invoke {@link #await}
     *        before the barrier is tripped
     * @param barrierAction the command to execute when the barrier is
     *        tripped, or {@code null} if there is no action
     * @throws IllegalArgumentException if {@code parties} is less than 1
     */
    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

await() 函数

1.CyclicBarrier 是可以被重用的。
2.CyclicBarrier 会响应中断,N 个线程还没有到齐,如果有线程收到了中断信号,所有阻塞的线程也会被唤醒。也就是 breakBarrier函数。然后count 被重置为初始值(parties),重新开始
3.构造函数传入的回调函数,barrierAction 只会被最后一个线程执行一次。

 public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }
    /**
     * Main barrier code, covering the various policies.
     */
    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;  //每个线程调用一次await(). count 减一,当count==0时,则唤醒其他的所有线程
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
                    if (command != null)// 一起唤醒之和,如果回调函数不为空,还需要执行回调函数
                        command.run();
                    ranAction = true;
                    nextGeneration();//唤醒其他所有线程,并将count值复原。
                                     //用于下一次的CyclicBarrier.这是可以复用的原因
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }

            // loop until tripped, broken, interrupted, or timed out
            //当count>0,说明 人没有到齐,需要阻塞自己
            for (;;) {
                try {
                    if (!timed)
                        trip.await();//当阻塞自己的时候,await方法会释放锁,这样其他线程调用await方法时会执行--count
                    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.
                        //如果不是响应的中断,说明是被 sigalAll唤醒。则自己唤醒
                        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();
        }
    }
     private void nextGeneration() {
        // signal completion of last generation
        // 唤醒所有阻塞的线程
        trip.signalAll();
        // set up next generation
        // 设置初始值,开始下一个轮回
        count = parties;
        generation = new Generation();
    }

业务场景

10 个求职者一起来公司应聘,招聘方式为笔试和面试。首先,需要等10个人到期后,开始笔试,笔试结束之后,再一起参加面试。把10个人看作10个线程。如图所示:
在这里插入图片描述

方案一:

采用一个CyclicBarrier.重复实现两次等待

代码实现

class Solver {
    public static void main(String[] args) {
         CyclicBarrier barrier=new CyclicBarrier(10);
         for (int i=0;i<10;i++){
             //开启10个线程模拟10个求职者
             new Thread(new JobHunt(barrier)).start();
         }
    }
}


    class JobHunt implements Runnable {
        private CyclicBarrier cyclicBarrier;

        public JobHunt(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            //赶来公司路上
            doOnTheWay();
            //到公司后,看人是否到齐,如果没有到齐,就阻塞,
            // 到齐了就开始笔试
            try {
                System.out.println(Thread.currentThread().getName()+" 已经来公司了...");
                cyclicBarrier.await();
                doWriteExam();
                System.out.println(Thread.currentThread().getName()+" 笔试做完了....");
                cyclicBarrier.await();
                doInterview();
                System.out.println(Thread.currentThread().getName()+"  面试完啦.....");

            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }

        /**
         * 模拟在路上方法
         */
        public void doOnTheWay(){
            doCostTime(2000);
        }

        /**
         * 模拟笔试过程
         */
        public void doWriteExam(){
            doCostTime(3000);
        }

        /**
         * 模拟面试过程
         */
        public void doInterview(){

            doCostTime(5000);
        }


        private void doCostTime(int time){
            Random random=new Random();
            try {
                //随机休眠时间
                int count=random.nextInt(time);
               // System.out.println(count);
                Thread.sleep(count);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

测试截图

从截图中我们可以看出,CyclicBarrier 实现了大家一起等待,直至人到齐了再去一起做笔试或者面试。
在这里插入图片描述

方案二

由于两次等待结束后,打印的消息不一样。所以我们采用两个 CyclicBarrier。分别传入不同的 barrierAction,来实现自定义的 等待结束后的打印事件。

代码实现

class Solver {
    public static void main(String[] args) {
         //将笔试等待的回调函数传入
         CyclicBarrier barrierOnWriteExam=new CyclicBarrier(10,new BarrierActionOnWriteExam());
         //将面试等待的回调函数传入
         CyclicBarrier barrierOnInterview=new CyclicBarrier(10,new BarrierActionOnInterview());
         for (int i=0;i<10;i++){
             //开启10个线程模拟10个求职者
             new Thread(new JobHunt(barrierOnWriteExam,barrierOnInterview)).start();
         }
    }
}


    class JobHunt implements Runnable {
        private CyclicBarrier cyclicBarrierOnWriteExam;
        private CyclicBarrier cyclicBarrierOnInterview;

        public JobHunt(CyclicBarrier cyclicBarrierOnWriteExam,CyclicBarrier cyclicBarrierOnInterview) {
            this.cyclicBarrierOnWriteExam = cyclicBarrierOnWriteExam;
            this.cyclicBarrierOnInterview=  cyclicBarrierOnInterview;
        }

        @Override
        public void run() {
            //赶来公司路上
            doOnTheWay();
            //到公司后,看人是否到齐,如果没有到齐,就阻塞,
            // 到齐了就开始笔试
            try {
                System.out.println(Thread.currentThread().getName()+" 已经来公司了...");
                cyclicBarrierOnWriteExam.await();
                doWriteExam();
                System.out.println(Thread.currentThread().getName()+" 笔试做完了....");
                cyclicBarrierOnInterview.await();
                doInterview();
                System.out.println(Thread.currentThread().getName()+"  面试完啦.....");

            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }

        /**
         * 模拟在路上方法
         */
        public void doOnTheWay(){
            doCostTime(2000);
        }

        /**
         * 模拟笔试过程
         */
        public void doWriteExam(){
            doCostTime(3000);
        }

        /**
         * 模拟面试过程
         */
        public void doInterview(){

            doCostTime(5000);
        }


        private void doCostTime(int time){
            Random random=new Random();
            try {
                //随机休眠时间
                int count=random.nextInt(time);
               // System.out.println(count);
                Thread.sleep(count);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    class BarrierActionOnWriteExam implements Runnable{

        @Override
        public void run() {
            //自定义等待完成后的回调函数
            System.out.println("大家人到齐了,开始笔试吧");
        }
    }

class BarrierActionOnInterview implements Runnable{

    @Override
    public void run() {
        //自定义等待完成后的回调函数
        System.out.println("大家人到齐了,开始面试吧");
    }
}

测试打印

通过打印结果可以看到,首先是能正确实现效果。其次 是通过传入 回调事件参数给 CyclicBarrier,可以很方便实现 自己的业务逻辑。
在这里插入图片描述

总结

虽然 CountDownLatch 和CyclicBarrier 都能实现多个线程一起等待然后一起做某些事情。
CountDownLatch 更多的是 一个主线程等待 分支线程完成。然后主线程去做其他事情。
CyclicBarrier 是 大家分别做某些事情,等每个人都做完后,大家再一起去做另外一件事情。
并且两者实现的 原理完全不同。
希望通过本文大家能对 CyclicBarrier 有个更加理性的认识。多敲敲小demo。看能否有优化的地方。这样才能更好的理解。
CountDownLatch 学习的地址:
https://blog.csdn.net/echohuangshihuxue/article/details/129280219

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/382335.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

完全彻底卸载Oracle

一、停止使用Oracle的服务停用oracle服务&#xff0c;进入计算机管理&#xff0c;在服务中&#xff0c;找到oracle开头的所有服务&#xff0c;右击选择停止。二、打开Universal Installer工具运行卸载Oracle数据库程序&#xff08;1&#xff09;、一般情况运行Oracle自带的卸载…

代谢组学:Microbiome又一篇!绘制重症先天性心脏病新生儿肠道微生态全景图谱

文章标题&#xff1a;Mapping the early life gut microbiome in neonates with critical congenital heart disease: multiomics insights and implications for host metabolic and immunological health 发表期刊&#xff1a;Microbiome 影响因子&#xff1a;16.837…

热烈祝贺|酒事有鲤盛装亮相2023中国(山东)精酿啤酒产业发展创新论坛暨展览会

酒事有鲤&#xff08;济南&#xff09;品牌管理有限公司是一家致力于将世界顶级精酿啤酒技术和理念与“ 在地”文化有机融合&#xff0c;做世界认 可的多元化好啤酒&#xff0c;通过精致 舒适的家门口酒馆&#xff0c;让啤酒的 世界观更为完整。 中国生物发酵产业协会联合齐鲁…

Gitlab普通用户转管理员

GitLab是常用的分部式代码库版本开源软件&#xff0c;默认系统中只有一个管理员。在工作中&#xff0c;如果有多个项目&#xff0c;则需要多个管理员分别管理各个的代码仓库&#xff0c;需要把多个普通用户配置成管理员&#xff0c;在Gitlab页面上&#xff0c;不能直接通过操作…

【编程基础之Python】7、Python基本数据类型

【编程基础之Python】7、Python基本数据类型Python基本数据类型整数&#xff08;int&#xff09;基本的四则运算位运算比较运算运算优先级浮点数&#xff08;float&#xff09;布尔值&#xff08;bool&#xff09;字符串&#xff08;str&#xff09;Python数据类型变换隐式类型…

tensorflow2.4--2.回归问题分析

文章目录前言流程案例操作前言 流程 回归问题预测连续值,在某个区间内变动. 常见的线性回归问题模型是yaxb,然而现实世界由于大量的数据偏差以及复杂度,同时还有大量的噪声,往往达不到如此的精确解,实际解决问题时需要考虑噪声的存在 对于噪声,往往我们已经假设了它符合高斯…

springboot内嵌Tomcat 安全漏洞修复

漏洞扫描提示的是tomcat-embed-core[CVE-2020-1938]&#xff0c;解决方式是升级tomcat的版本。 该漏洞影响的版本&#xff1a; Apache Tomcat 9.x < 9.0.31 Apache Tomcat 8.x < 8.5.51 Apache Tomcat 7.x < 7.0.100 Apache Tomcat 6.x 其余的安全漏洞也可以通过…

hometown-h5-template 一个开箱即用的前端H5解决方案 【无标题】

前言 大家好&#xff0c;我是 HoMeTown&#xff0c;最近不很忙&#xff0c;整理一套架子出来&#xff0c;有兴趣朋友可以看看&#xff0c;我自己已经投入生产使用了&#xff0c;大家看个人情况&#xff0c;选择性使用 GitHub仓库。 ✨ hometown-h5-template &#x1f4a5; 轻…

JavaSE21-集合1-set

文章目录一、集合概念二、set集合1、set集合的特点2、HashSet2.1 特点2.2 创建对象2.3 常用方法2.4 遍历2.4.1 foreach遍历2.4.2 使用迭代器遍历2.4.3 转换为数组遍历一、集合概念 集合就是用于存储多个数据的容器。相对于具有相同功能的数组来说&#xff0c;集合的长度可变会…

速度计算-课后程序(JAVA基础案例教程-黑马程序员编著-第十二章-课后作业)

【案例12-2】&#xff1a;速度计算 【案例介绍】 1.案例描述 本案例要求使用反射技术编写一个速度计算程序&#xff0c;计算某种交通工具的行驶速度。现有两种工具&#xff1a;Bike和 Plane&#xff0c;其中Bike的速度运算公式为&#xff1a;A*B/C&#xff0c;Plane的速度运…

Maven多模块开发

POM主要功能 maven学习教程很多&#xff0c;就不在赘述可以参考以下网站&#xff0c;这里只说明maven实际运用。 https://blog.csdn.net/xwh3165037789/article/details/121545762 菜鸟教程 Maven POM POM是在使用Maven构建项目最重要的部分&#xff0c; POM 中所有信息位于&l…

史上体积最小、功能最多的VxRail即将上市!

有史以来      最小、最灵活、最多的      VxRail集群      Dell VxRail VD-4000重磅登场!    戴尔即将全球推出VxRail超融合基础架构系统——基于PowerEdge XR4000的VxRail VD-4000(计划今年2月底全球同步上市)。      VD-4000具备小型专用外形,这种新的外…

帮助小型企业实现业务增长的7种数字营销策略

数字营销一直在不断地变化和发展&#xff0c;在过去的几年里我们已经见识到了其迅猛的发展速度。虽然我们在数字营销中看到了一些新的趋势&#xff0c;但对于小型企业来说很难利用并发挥其优势。相比较大型企业&#xff0c;小型企业的预算和资源通常有限&#xff0c;所以他们很…

Qt插件开发总结5--主界面嵌入插件UI

文章目录一、前言二、效果展示三、嵌入插件UI1、插件接口文件添加UI指针2、插件子项目工程建立UI类3、插件类中创建UI类、使UI指针指向创建的UI类4、插件元信息中添加widget键值对&#xff0c;指示插件UI嵌入主界面中的位置5、主界面中预留接入点tabWidget6、插件管理器中元数据…

9 怎么登录VNC

1&#xff09;首先在ssh登录后启动vncserver。登陆后输入下面的指令来创建自己的VNC。 命令vncserver :16 –geometry 1900x1000 其中&#xff1a;16是分配的端口号&#xff0c;1900x1000是分辨率。如果没有响应&#xff0c;建议执行下面操作后再次重复上面操作。 命令&#xf…

拦截器和过滤器的区别是什么

过滤器 过滤器Filter是基于Servlet实现。Servlet的工作原理是拦截配置好的客户端请求&#xff0c;然后对Request和Response进行处理。Filter过滤器随着web应用的启动而启动&#xff0c;只初始化一次。 过滤器的配置比较简单&#xff0c;直接实现Filter 接口就可以了&#xff…

Python 虚拟环境的使用

PyCharm 创建的虚拟环境与使用 workon 命令创建的虚拟环境在本质上没有区别&#xff0c;它们都是 Python 的虚拟环境。 使用 PyCharm 创建工程时&#xff0c;使用可以使用曾经工程的虚拟环境&#xff0c;或者新建一个虚拟环境来安装 Python 的库&#xff0c;又或者使用 workon…

org.postgresql.util.PSQLException

1.使用springboot整合postgresql时报错&#xff1a;org.postgresql.util.PSQLException 1.1依赖 使用spring boot和postgresql搭建简单demo <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</arti…

Git概述

查看git版本&#xff1a; git --version1、Git概述 Git 是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。 Git 易于学习&#xff0c;占地面积小&#xff0c;性能极快。它具有廉价的本地库&#xff0c;方便的暂存区域和多个工作…

【C++基础入门】运算符、程序流程结构(if语句,for循环,switch语句等)

一&#xff1a;运算符 作用&#xff1a;用于执行代码的运算 运算符类型作用算术运算符用于处理四则运算赋值运算符用于将表达式的值赋给变量比较运算符用于表达式的比较&#xff0c;并返回一个真值或假值逻辑运算符用于根据表达式的值返回真值或假值 1.1 算术运算符 作用 &a…