面试官:说一说CyclicBarrier的妙用!我:这个没用过...

news2024/9/22 9:41:47

写在开头

面试官:同学,AQS的原理知道吗?
我:学过一点,抽象队列同步器,Java中很多同步工具都是基于它的…
面试官:好的,那其中CyclicBarrier学过吗?讲一讲它的妙用吧
我:啊,这个,这个我平时写代码没用过…
面试官:那你回去再学学吧!

随着Java的国内竞争环境逐渐激烈,面试时遇到很多奇葩的问题也是越来越多,以上是模拟的一个面试场景,同学们看下你们能答得上来不?😝

什么是CyclicBarrier?

在过去的几天里,我们基于AQS学习了不少内容,其中基于AQS构建的同步工具类也学了Semaphore(信号量)和CountDownLatch(倒计时器),甚至于也手撕过同步器,今天我们继续来学习另外一个同步类:CyclicBarrier

CyclicBarrier(循环屏障):让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。

CyclicBarrier的原理

在CyclicBarrier有两个成员变量分别为partiescount,前者代表每次拦截的线程数量,后者是初始化时保持和parties相等的计数标识,每有一个线程执行到同步点时,count减1,当count值变为0时说明所有线程都走到了同步点,这时就可以尝试执行我们在构造方法中设计的任务啦。

【源码解析1】

//每次拦截的线程数
private final int parties;
//计数器
private int count;

//一个参数的构造
public CyclicBarrier(int parties) {
    this(parties, null);
}
//多参构造,parties为拦截线程数,barrierAction这个 Runnable会在 CyclicBarrier 的计数器为 0 的时候执行,用来完成更复杂的任务。
public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}

每个线程通过调用await方法告诉CyclicBarrier已经到达屏障,然后进行阻塞等待,知道count等于0,所有线程都到达了屏障,因此,我们跟入await方法的源码中去看一下。

【源码解析2】

public int await() throws InterruptedException, BrokenBarrierException {
  try {
      return dowait(false, 0L);
  } catch (TimeoutException toe) {
      throw new Error(toe); // cannot happen
  }
}
//await方法内部,继续调用dowait方法实现功能
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();
        }
        // cout减1
        int index = --count;
        // 当 count 数量减为 0 之后说明最后一个线程已经到达栅栏了,也就是达到了可以执行await 方法之后的条件
        if (index == 0) {  // tripped
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand;
                if (command != null)
                    command.run();
                ranAction = true;
                // 将 count 重置为 parties 属性的初始化值
                // 唤醒之前等待的线程
                // 下一波执行开始
                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();
    }
}

在dowait(boolean timed, long nanos),可以通过时间参数来设置阻塞的时间,默认为false,在这个方法内部,每次线程调用await后,都会进行–count操作,直到index为0时,会去执行command,然后唤醒线程继续向下执行,CyclicBarrier 的计数器可以通过reset()方法重置,所以它能处理循环使用的场景。

CyclicBarrier的使用

大致的了解了CyclicBarrier的原理之后,我们写个小demo测试一下它如何使用

【代码示例】

public class Test {
    public static void main(String[] args) {
        int numberOfThreads = 3; // 线程数量
        CyclicBarrier barrier = new CyclicBarrier(numberOfThreads, () -> {
            // 当所有线程都到达障碍点时执行的操作
            System.out.println("所有线程都已到达屏障,进入下一阶段");
        });

        for (int i = 0; i < numberOfThreads; i++) {
            new Thread(new Task(barrier), "Thread " + (i + 1)).start();
        }
    }

    static class Task implements Runnable {
        private final CyclicBarrier barrier;

        public Task(CyclicBarrier barrier) {
            this.barrier = barrier;
        }

        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + " 正在屏障处等待");
                barrier.await(); // 等待所有线程到达障碍点
                System.out.println(Thread.currentThread().getName() + " 已越过屏障.");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

输出:

Thread 2 正在屏障处等待
Thread 1 正在屏障处等待
Thread 3 正在屏障处等待
所有线程都已到达屏障,进入下一阶段
Thread 3 已越过屏障.
Thread 1 已越过屏障.
Thread 2 已越过屏障.

结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

在这里插入图片描述
如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

在这里插入图片描述

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

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

相关文章

Sonar下启动发生错误,elasticsearch启动错误

Download | SonarQube | Sonar (sonarsource.com) 1.首先我的sonar版本为 10.4.1 &#xff0c;java版本为17 2.sonar启动需要数据库,我先安装了mysql, 但是目前sonar从7.9开始不支持mysql&#xff0c;且java版本要最少11,推荐使用java17 3.安装postsql,创建sonar数据库 4.启…

什么是5G单兵?5G单兵跟5G执法记录仪有什么异同?

深入了解5G单兵系统&#xff1a;未来执法新革命与执法记录仪的差异分析 随着技术的不断进步和发展&#xff0c;5G技术的应用已经渗透到社会的各个领域中&#xff0c;包括公安、消防等执法与紧急救援领域。其中&#xff0c;5G单兵系统作为一种新型的智能化执法装备&#xff0c;…

树形侧边栏(展开、全选、切换名称)

父文件&#xff1a; index.vue <template><div class"h-full p20px bg-#f5f5f5"><ContentWrap class"w-260px h-[calc(100vh-200px)] min-h-700px"><TenantTree select"tentantSelect" /></ContentWrap></div&…

【计算机考研】跨考计算机,需要准备多久才来得及?

9个月跨考计算机&#xff0c;如果选择是408的话&#xff0c;时间稍微有点紧张&#xff0c;前期感觉不大&#xff0c;后期数学408堆在一起会感觉很难受... 很多确定考408的同学都是一开始先从数据结构开始复习的&#xff0c;这样到了中后期觉得自己时间不够了再去转自命题也来得…

MySQL——基础

SQL 全称 Structured Query Language&#xff0c;结构化查询语言。操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库统一标准 。 SQL 通用语法 SQL语句可以单行或多行书写&#xff0c;以分号结尾。SQL语句可以使用空格/缩进来增强语句的可读性。MySQL数据库…

【C++】unordered_map unordered_set 底层刨析

文章目录 1. 哈希表的改造2. unordered_map3. unordered_set C STL 库中&#xff0c;unordered_map 和 unordered_set 容器的底层为哈希表&#xff0c;本文将简单模拟哈希表&#xff08;哈希桶&#xff09;&#xff0c;unordered_map 和 unordered_set 只需封装哈希表的接口即可…

分布式的计算框架之Spark(python第三方库视角学习PySpark)

基本介绍 Apache Spark是专为大规模数据处理而设计的快速通用的计算引擎 。现在形成一个高速发展应用广泛的生态系统。 特点介绍 Spark 主要有三个特点&#xff1a; 首先&#xff0c;高级 API 剥离了对集群本身的关注&#xff0c;Spark 应用开发者可以专注于应用所要做的计…

C语言面试题之奇偶链表

奇偶链表 实例要求 1、给定单链表的头节点 head &#xff0c;将所有索引为奇数的节点和索引为偶数的节点分别组合在一起&#xff0c;然后返回重新排序的列表&#xff1b;2、第一个节点的索引被认为是 奇数 &#xff0c; 第二个节点的索引为 偶数 &#xff0c;以此类推&#x…

快速掌握Spring监控(Spring Boot admin)

监控 监控可视化监控平台Admin底层逻辑info 自定义端点 监控 监控的作用&#xff1a; 监控服务状态是否宕机监控服务运行指标&#xff08;内存&#xff0c;虚拟机&#xff0c;线程&#xff0c;请求等&#xff09;监控日志管理服务&#xff08;服务下线&#xff09; 监控的实…

详解IP证书申请

申请IP证书&#xff0c;也被称为IP SSL证书&#xff0c;是一种特殊的SSL证书&#xff0c;它不同于传统的域名验证&#xff08;DV&#xff09;证书&#xff0c;是通过验证公网IP地址而不是域名来确保安全连接。这种证书用于保护IP地址&#xff0c;并在安装后起到加密作用。以下是…

李廉洋:4.16黄金,原油最新资讯,亚盘面走势分析及策略。

周一&#xff0c;10年期美国国债收益率攀升3个基点&#xff0c;至4.55%&#xff0c;原因是投资者在权衡中东紧张局势加剧演变为全面地区战争的风险。美国国债将成为市场关注的焦点&#xff0c;上周美国国债收益率上升12个基点&#xff0c;至去年11月以来的最高水平&#xff0c;…

TC387实现SPI自通讯

TC387实现SPI自通讯 预期效果&#xff1a; TC387上定义两个SPI通讯接口&#xff0c;一个用于发数据一个用于收数据。准确无误的收到一次数据就对核心板led灯的状态进行一次翻转。 由于实验设备有限&#xff0c;只能想办法通过现有设备进行实验。 实现过程&#xff1a; 最开…

【学习笔记PPT摘录】lan.289.24.4-15

1.纪念品分组.双指针-01 #include <bits/stdc++.h> using namespace std;int A[40000];// 纪念品价值均衡// 把购来的纪念品进行分组 之和不超过整数 w// 每组只能有两个纪念品 希望分组的数目要少// 贪心的策略就是 每个较大的数找到一个 最大的较小的数使其能够小于w//…

基于注解以及配置类使用SpringIoc

四 基于注解方式使用SpringIoc 和 XML 配置文件一样&#xff0c;注解本身并不能执行&#xff0c;注解本身仅仅只是做一个标记&#xff0c;具体的功能是框架检测到注解标记的位置&#xff0c;然后针对这个位置按照注解标记的功能来执行具体操作。 本质上&#xff1a;所有一切的…

京东商品详情API接口(商品属性丨sku价格丨详情图丨标题等数据)

京东商品详情API接口是京东开放平台提供的一种API接口&#xff0c;通过调用该接口&#xff0c;开发者可以获取京东商品的标题、价格、库存、月销量、总销量、详情描述、图片等详细信息。下面针对您提到的商品属性、SKU价格、详情图以及标题等数据&#xff0c;做具体介绍&#x…

c++ 多文件编程

1.结构目录 声明类:用于声明方法,方便方法管理和调用&#xff1b; 实现类:用于实现声明的方法; 应用层:调用方法使用 写过java代码的兄弟们可以这么理解&#xff1a; 声明类 为service层 实现类 为serviceimpl层 应用层 为conlloter层 2.Dome 把函数声明放在头文件xxx.h中&…

STM32学习和实践笔记(5):时钟树

STM32一共有4个时钟源。外部时钟高低速各一个&#xff0c;内部时钟高低速各一个。 外部高速时钟是&#xff1a;4-16MHZ的HSE OSC。HS表示高速high speed. E表示外部的external。开发板该处安装的8M晶振。 外部低速时钟是&#xff1a;32.768KHz的LSI OSC。LS表示高速low speed…

1.JAVASE练习题(递归篇)

1.递归求解汉诺塔问题 public static void move(char pos1,char pos2) {System.out.print(pos1"->"pos2" "); }public static void hanoi(int n,char pos1,char pos2,char pos3) {if(n 1) {move(pos1,pos3);return;}hanoi(n-1,pos1,pos3,pos2);move(p…

六、数据呈现

目录 6.1 理解输入输出 6.1.1 标准文件描述符 1 STDIN &#xff08;0&#xff09; 2 STDOUT &#xff08;1&#xff09; 3 STDERR&#xff08;2&#xff09; 6.1.2 重定向错误 1 只重定向错误 2 重定向错误和数据 6.2 在脚本中重定向输出 6.2.1 临时重定向 6.2.2 永…

项目——boost搜索引擎

今天我们来写一个boost搜索引擎&#xff01; &#xff08;后续如果有更新&#xff0c;这个博客也会更新&#xff09; gitee连接:boost搜索引擎: boost搜索引擎 首先我们要介绍一下我们这个项目&#xff0c;我们项目的目的是通过我们的搜索引擎能够通过关键字查找到对应的网页…