CyclicBarrier底层源码解析

2023-10-26

一、概述

前面我们讲解了ReentrantLock,CountDownLatch,Semaphore的源码,他们都是由AQS来实现的,而CyclicBarrier则是通过ReentrantLock+Condition实现的
CyclicBarrier即为栏栅,比如短跑比赛,5人一组,必须5人到齐准备好了了,才开始跑,5人跑走以后,后面第二组同样等到齐准备好了,继续开始跑。我们就可以把人比作线程。

二、CyclicBarrier框架

	private static class Generation {
        boolean broken = false;
    }

    /** 定义一个可重入锁*/
    private final ReentrantLock lock = new ReentrantLock();
    /** 定义一个 Condition */
    private final Condition trip = lock.newCondition();
    /** 定义栏栅parties */
    private final int parties;
    /** 定义执行线程 */
    private final Runnable barrierCommand;
    /** 定义generation */
    private Generation generation = new Generation();
     /** 定义count */
	private int count;
  • public int await() //调用的线程被挂起,知道满足珊栏的条件
  • public int await(long timeout, TimeUnit unit)
    //调用的线程被挂起,知道满足珊栏的条件或者时间达到传

入的值

三、源码解读

定义一个栏栅为3,A,B,C三个线程分别去执行,A,B线程先准备完成,C线程10s后准备完成
最后要等C线程准备完成后才执行

		CyclicBarrier cyclicBarrier = new CyclicBarrier(3);

        new Thread(()->{
            try {
                System.out.println("A 准备完成 ----------");
                cyclicBarrier.await();
                System.out.println("A 执行 ----------");

            }catch (Exception e){
            }
        },"A").start();
        new Thread(()->{
            try {
                System.out.println("B 准备完成 ----------");
                cyclicBarrier.await();
                System.out.println("B 执行 ----------");
            }catch (Exception e){
            }
        },"B").start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(10);
                System.out.println("C 准备完成 ----------");
                cyclicBarrier.await();
                System.out.println("C 执行 ----------");
            }catch (Exception e){
            }
        },"C").start();

分析源码,进入await();方法

public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }

进入dowait(false, 0L);

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)//Generation维护了一个broken变量,表示是否打破屏障。如果屏障已经打破或线程已经停止则都会抛出异常。
                throw new BrokenBarrierException();
            if (Thread.interrupted()) {//线程中断
                breakBarrier();//唤醒其他线程
                throw new InterruptedException();//抛出异常
            }
            int index = --count;//计数-1并获取
            if (index == 0) {  
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;//构造方法传入的一个Runnable
                    if (command != null)
                        command.run();//若有传入,则唤醒所有线程前先执行指定的任务
                    ranAction = true;
                    nextGeneration();//到达栏栅,count=0,唤醒其他线程
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();//唤醒其他线程
                }
            }
            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 {
                        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();
        }
    }

首先A线程 进入方法,加锁,判断是否打破屏障,是否中断等(特殊情况),若都没有则判断count的值
count初始化=parties=2,index=–count=2 不为0则进入下一步自旋,调用trip.await();线程挂起,释放锁。接下来B线程 进入方法,加锁,判断是否打破屏障,是否中断等(特殊情况),若都没有则判断count的值
count=1,index=–count=1 不为0则进入下一步自旋,调用trip.await();线程挂起,释放锁
10s后C线程进入 count=0,index=–count=0,进入nextGeneration();方法

private void nextGeneration() {
        // 唤醒其他挂起的线程
        trip.signalAll();
        // 设置count为初始值,便于重复使用
        count = parties;
        generation = new Generation();// 重新维护Generation
    }

此时ABC三个线程都可以执行了

再来说说trip.await();方法,属于Condition接口,在AQS中实现的

		public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();//将当前线程包装下后
            //添加到Condition自己维护的一个链表中。
            int savedState = fullyRelease(node);//释放当前线程占有的锁
            //调用await前,当前线程是占有锁的
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {//释放完毕后,遍历AQS的队列,看当前节点是否在队列中
            //不在 说明它还没有竞争锁的资格,所以继续将自己沉睡。
			//直到它被加入到队列中,聪明的你可能猜到了,
			//没有错,在singal的时候加入不就可以了?
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            //被唤醒后,重新开始正式竞争锁,同样,如果竞争不到还是会将自己沉睡,等待唤醒重新开始竞争
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

核心还是LockSupport.park(this);挂起线程
trip.signalAll();也在AQS中实现

public final void signalAll() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;//firstWaiter为condition自己维护的一个链表的头结点,
	        //取出第一个节点后开始唤醒操作
            if (first != null)
                doSignalAll(first);
        }

四、总结

CyclicBarrier的核心就是await()方法等待后,执行nextGeneration()唤醒所有线程
CyclicBarrier底层基于ReentrantLock和Condition实现,如果count不为0,则调用Condition的await方法
让线程等待执行,当count为0时,调用Condition的singleAll唤醒全部等待的线程执行

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CyclicBarrier底层源码解析 的相关文章

  • Ubuntu安装GCC5/7/9/10/11

    为了使用C 14 17 20的新特性 我们难免要升级下自己的GCC版本 同时还要保证自己新安装的GCC生效 并且和原GCC共存 安装GCC 5 0 sudo add apt repository ppa ubuntu toolchain r
  • 编程思维可以有效简化问题

    我们可能会遇到这样一些情况 例如某个孩子和同龄人相比 说话做事更有条理性 每一句都清清楚楚 在逻辑性上明显较强 这是为什么呢 格物斯坦小坦克认为这背后都是因为逻辑思维能力的差异化 逻辑思维差导致孩子处理问题的能力差 无法正确表达自己的想法
  • LaTeX的安装教程(Texlive 2020 + TeX studio)

    LaTeX 音译为 拉泰赫 是一种基于 的排版系统 由美国计算机学家莱斯利 兰伯特 Leslie Lamport 在20世纪80年代初期开发 利用这种格式 即使使用者没有排版和程序设计的知识也可以充分发挥由TeX所提供的强大功能 能在几天
  • 【python学习笔记】:几个 Python 项目构建工具

    Python 历时这么久以来至今还未有一个事实上标准的项目管理及构建工具 以至于造成 Python 项目的结构与构建方式五花八门 这或许是体现了 Python 的自由意志 不像 Java 在经历了最初的手工构建 到半自动化的 Ant 再到
  • 善于使用二阶思维

    事情往往不是你想象的那样 有时候 看似解决了问题 却在不经意间 引发了更严重的后果 帮助我们思考 决策 解决问题的最有效方法是 运用二阶思维 什么是二阶思维 一阶思维是单纯而肤浅的 几乎人人都能做到 二阶思维则是深邃 复杂而迂回的 能做到的
  • 异步调用,async await基本示例及项目中运用

    示例 async function asyncCall let a await interfaceFn console log 这里是同步 function interfaceFn return new Promise resolve re
  • Jackson中的自定义反序列化器和验证

    tl dr 将输入验证添加到Jackson中的自定义json解串器很重要 在RHQ中 我们在几个地方使用了Json解析 直接在as7 Wildfly插件中 或者通过RESTEasy 2 3 5间接在REST api中使用 已经很繁重了 现在
  • 构建高性能应用:Java与MongoDB的完美融合

    AI绘画关于SD MJ GPT SDXL百科全书 面试题分享点我直达 2023Python面试题 2023最新面试合集链接 2023大厂面试题PDF 面试题PDF版本 java python面试题 项目实战 AI文本 OCR识别最佳实践 A
  • windows 资源管理器已停止工作的解决办法

    问题现象描述 电脑开机输入密码后 加载很久 才跳转至解锁后的界面 但解锁后的界面不显示桌面 任务管理器提示 资源管理器已停止工作 尝试联机解决方案或者关闭程序都不奏效 一些快捷键比如win r win e都不起作用 但能ctr alt de
  • vue中 Error in mounted hook: "TypeError: __WEBPACK_IMPORTED_MODULE_0__assets_swiper_js__.default is n...

    个人小站点 https sundjly github io 在vue的项目中出现了以下错误 Error in mounted hook TypeError WEBPACK IMPORTED MODULE 0 assets swiper js
  • matplotlib可视化之饼图plt.pie()与plt.legend()中bbox_to_anchor参数的理解

    函数功能 表示离散变量各占比情况 调用方法 plt pie x explode None labels None colors None autopct None pctdistance 0 6 shadow False labeldist
  • matlab 小球随机分布,matlab空间内生成随机球体,并不重叠

    根据comsol上的奶酪模型 即在三维空间内生成小孔模型 想进一步控制生成的小孔互不重叠 但是运行不出来 大神们能帮我看看错误吗 代码是循环体部分的 奶酪中的小孔初始化参数 number of hols 80 初始化并定义奶酪内的小孔总数
  • matlab读取txt文档三行数据库,matlab将数据读取和写入txt文档

    matlab中打开文件 fid fopen 文件名 打开方式 说明 fid用于存储文件句柄值 若是fid gt 0 这说明文件打开乐成 另外 在这些字符串后添加一个 t 如 rt 或 wt 则将该文件以文本方式打开 若是添加的是 b 则以二
  • adb复制root到手机,怎样通过adb命令来root手机

    实战 Androidadb常用命令详解 ADB 全称Android Debug Bridge 是一个功能非常强大的工具 它位于Android SDK安装目录的platform tools 子目录下 ADB工具即可完成模拟器文件与电脑文件的相
  • win10 服务主机:DCOM服务器进程启动器 进程导致电脑卡死解决思路

    新买的笔记本 联想拯救者Y7000 系统 win10专业版 已经禁用了网上可搜的服务 没有win10开始菜单的磁条 原因 总是在开机一段时间后系统卡死 只能强制关机才可以 查找 后来开着任务管理器 放着看到底什么原因造成的 发现 服务主机
  • Ubuntu 16.04 上 CUDA_10.0及cuDNN的安装

    GPU Geforce GTX1060 驱动版本 418 56 最开始打算装CUDA 10 1 nvidia与cuda需相匹配 但是在运行cuda run后出现的用户许可证信息有问题 如图 但是CUDA 10 1与驱动版本是相匹配的 也没有
  • 排序算法总结(长期更新)

    1 最基础的排序算法 1 冒泡排序 思想 每次使用交换的方式将剩余元素中较大的元素放到一端 直到剩余元素为0的时候排序结束 include
  • PCL读取显示点云的两种方式

    读取点云的两种方式 PCL提供了两种pcd点云读写方式 其中PCD Point Cloud Date 点云数据 对应的文件格式为 pcd 是 PCL官方指定格式 具有 ASCII 和 Binary 两种数据存储类型 其中 ASCII 格式的
  • snipaste便捷截图小工具

    Snipaste是一个简单但强大的截图工具 也可以让你将截图贴回到屏幕上 下载并打开Snipaste 按下F1来开始截图 再按F3 截图就在桌面置顶显示了 还可以将剪贴板里的文字或者颜色信息转化为图片窗口 并且将它们进行缩放 旋转 翻转 设

随机推荐

  • 原码 反码 补码 移码

    原码 反码 补码都是有符号定点数的表示方法 一个有符号定点数的最高位为符号位 0是正 1是负 反码 原码 除符号位外 每位取反 补码 反码 1 反码 补码 1 移码 补码符号位取反 原码就是这个数本身的二进制形式 正数的反码和补码都是和原码
  • cmake错误:“未定义的引用”,“未声明的引用”

    描述 在编译代码的时候 经常会出现 未声明的引用 和 未定义的引用 之类的错误 原因 这种情况一般在编译的过程是不会出现问题 在链接的过程会出现这些问题 未声明的引用一般是头文件引入错误 或者找不到头文件 未定义的引用应该是找不到函数的实现
  • omv5-gitlab/gitlab-ce deploy

    omv5 gitlab gitlab ce yml example version 3 6 services web image gitlab gitlab ce latest restart always environment GITL
  • AWS架构图更新2019 - 最强大的AWS架构设计工具

    需要设计您的Amazon Web Services AWS 架构 今天我想介绍一下如何使用Visual Paradigm的云架构设计软件绘制AWS架构图快速而有趣 他们提供世界上最简单 最强大的AWS架构设计工具 全套AWS符号 创建专业的
  • Python-装饰器详解

    装饰器 介绍 在Python中 装饰器是一种特殊的语法 用于修改或增强函数的功能 装饰器是Python的高级特性之一 它允许我们通过在不修改原函数代码的情况下 添加额外的功能或行为 装饰器是一个函数 它接受一个函数作为参数 并返回一个新的函
  • 构建工具Maven/Gradle

    Maven Maven项目对象模型 POM 可以通过一小段描述信息来管理项目的构建 报告和文档的项目管理工具软件 Maven 除了以程序构建能力为特色之外 还提供高级项目管理工具 由于 Maven 的缺省构建规则有较高的可重用性 所以常常用
  • 微信小程序之微信登录

    在开发微信小程序的时候 我们经常需要用到微信登录 通过wx的接口来获取微信用户的信息 然后存储到我们数据库来创建属于我们系统的用户信息 所以我们就要使用到wx login wx getUserProfile 这两个方法 wx login w
  • 向量点乘叉乘等理解和应用

    https baike baidu com item E5 90 91 E9 87 8F 1396519 fr aladdin 1 标量和矢量 2 1 2 3 2 4 6 2 4 6 2 1 2 3 2 矢量和矢量的加减 三角形定则解决向量
  • 程序员3年5年10年三个阶段

    第一阶段 三年 三年对于程序员来说是第一个门槛 这个阶段将会淘汰掉一批不适合写代码的人 这一阶段 我们走出校园 迈入社会 成为一名程序员 正式从书本上的内容迈向真正的企业级开发 我们知道如何团队协作 如何使用项目管理工具 项目版本如何控制
  • 奇偶剪枝算法

    奇偶剪枝是数据结构的搜索中 剪枝的一种特殊小技巧 现假设起点为 sx sy 终点为 ex ey 给定t步恰好走到终点 如图所示 竖走 横走 转弯 易证abs ex sx abs ey sy 为此问题类中任意情况下 起点到终点的最短步数 记做
  • 弱网使用教程

    Network Emulation for Windows Toolkit 1 下载安装包 默认安装 下载地址 https drive google com file d 1jvviwEDRx2UtmpUxe1 Sy94E3wBao5Ot
  • 基于51单片机的红外解码器

    1 简介 本红外解码器是以MCS 51系列AT89C512片机为核心 将红外传感器接收的信号解析出来 LCD1602显示屏将解码数据显示出来 2 总体原理图 硬件组成 单片机最小系统 LCD1602显示屏 IR红外接收器 系统电源 3 程序
  • 数据结构---n皇后问题

    n皇后问题 题目描述 JAVA实现 力扣提交代码 n皇后问题 题目描述 对于四皇后问题的解 放置一个皇后 棋盘被占据的解为 很明显 每行只能放置 且只能放置一个皇后 代码中 queen row 1 来实现的 一个完整的示例 放第一个皇后 放
  • 抖音将会输给快手?时间会证明一切

    为什么快手产品的主界面上没有设置频道分类 这样带来的用户体验真的好吗 在快手一个月之前的员工大会上 入职不久的新员工赵波提出了这个疑问 这个问题不只是他一个人的 之前官方的解答是 不意给用户设置标签是为了保持界面简洁 不对内容做过多的评判
  • i.MX6ULL - 从零开始移植uboot-imx_v2020.04_5.4.70_2.3.0

    i MX6ULL 从零开始移植uboot imx v2020 04 5 4 70 2 3 0 目录 i MX6ULL 从零开始移植uboot imx v2020 04 5 4 70 2 3 0 前言 1 环境搭建 2 NXP官方原版UBOO
  • 一文解决VS Code安装、C++环境配置、OpenCV配置

    前言 本文包括VScode安装 C 环境配置以及OpenCV配置全过程 VS Studio配置OpenCV比较简单 可以直接使用OpenCV官网已有的用VS Studio编译器编译好的OpenCV库 但VS Code不能直接利用VS Stu
  • Android:基本程序单元 Activity

    Activity 概述 在 Android 应用中 提供了 4 大基本组件 分别是 Activity Service BroadcastReceiver 和 ContentProvider 而 Activity 是 Android 应用最常
  • QApplication与QCoreApplication

    QApplication GUI 程序中 有且仅有一个 QApplication 类 管理GUI程序的控制流和主设置 QApplication 包含主事件循环 所有来自窗口系统和其他源的事件将被处理和分配 它也处理程序的初始化 析构和提供会
  • **vue.esm.js?efeb:591 [Vue warn]: Invalid prop: type check failed for prop "data". Expected Array

    vue esm js efeb 591 Vue warn Invalid prop type check failed for prop data Expected Array got String 有可能是这几种情况
  • CyclicBarrier底层源码解析

    一 概述 前面我们讲解了ReentrantLock CountDownLatch Semaphore的源码 他们都是由AQS来实现的 而CyclicBarrier则是通过ReentrantLock Condition实现的 CyclicBa