深入理解Java并发工具包中的CyclicBarrier

在这里插入图片描述

码到三十五 : 个人主页

心中有诗画,指尖舞代码,目光览世界,步履越千山,人间尽值得 !


在Java的并发编程世界中,协调和管理多个线程的执行是一项复杂而关键的任务。为了简化这一挑战,Java并发包(java.util.concurrent,简称JUC)提供了一系列强大的同步工具,其中CyclicBarrier(循环栅栏)是一个特别有趣且实用的类。本文将深入探讨CyclicBarrier的内部机制、使用场景,以及它与其他同步原语的区别和联系。

目录

    • 前言
    • 一、CyclicBarrier的内部机制
    • 二、源码分析CyclicBarrier的实现原理
      • 2.1 主要属性和构造函数
      • 2.2 await()方法
    • 二、CyclicBarrier的使用
      • 2.1 CyclicBarrier使用场景
      • 2.2 CyclicBarrier实现并行计算任务
    • 三、CyclicBarrier与CountDownLatch的区别与联系
    • 四、总结

前言

CyclicBarrier的字面意思是“可循环使用的屏障”。它允许一组线程互相等待,直到所有线程都到达一个公共的屏障点(或称为同步点)。在这个屏障点上,线程会被阻塞,直到所有参与的线程都到达这个点。一旦所有线程都到达屏障点,屏障就会被打开,允许所有线程继续执行。
在这里插入图片描述

这个“循环”的概念意味着,一旦所有线程通过屏障,屏障就会自动重置,可以再次用于下一轮的线程同步。这使得CyclicBarrier非常适合于那些需要多次同步的场景。

一、CyclicBarrier的内部机制

CyclicBarrier的内部实现基于一个计数器和一个条件变量(通常是一个锁和相关的等待/通知机制)。每当一个线程调用await()方法时,它会首先检查计数器的值是否达到了在创建CyclicBarrier时指定的“阈值”(即需要等待的线程数)。如果计数器尚未达到阈值,线程就会被阻塞,并等待其他线程的到来。

当另一个线程也调用await()方法时,计数器的值会增加,并且会再次检查是否达到了阈值。如果达到了阈值,那么所有等待在屏障点的线程都会被唤醒,并继续执行。此时,计数器会被重置为0,屏障进入下一轮的使用。

此外,CyclicBarrier还提供了一个可选的Runnable参数。当所有线程都到达屏障点时,这个Runnable任务会在最后一个到达屏障点的线程中执行。这通常用于进行一些额外的初始化、汇总或清理工作。

需要注意的是,如果某个线程在等待过程中因为中断或异常而退出,那么所有等待在屏障点的线程都将收到一个BrokenBarrierException异常。这是因为屏障已经被“破坏”,无法再保证所有线程都能正常通过。

二、源码分析CyclicBarrier的实现原理

CyclicBarrier允许一组线程互相等待,直到所有线程都到达某个公共屏障点(barrier point)。为了深入理解其实现原理,我们将结合CyclicBarrier的源码进行分析。

2.1 主要属性和构造函数

CyclicBarrier的主要属性包括:

  • parties:表示必须调用await()方法的线程数量,即屏障的阈值。
  • count:当前已到达屏障的线程数量。
  • barrierCommand:当所有线程到达屏障时执行的可选任务。
  • generation:用于标识当前屏障的“代”或循环次数。每当屏障被打破或所有线程通过屏障时,它都会增加。

构造函数允许设置parties(必须到达的线程数)和可选的barrierAction(所有线程到达屏障时执行的任务)。

2.2 await()方法

await()方法是CyclicBarrier的核心。当线程调用此方法时,它会执行以下步骤:

  1. 检查是否有线程由于中断或异常而退出,导致屏障处于“破坏”状态。如果是,则抛出BrokenBarrierException

  2. 如果当前线程不是最后一个到达屏障的线程,则将其放入等待队列中,并可能因等待而被挂起。

  3. 如果当前线程是最后一个到达屏障的线程,则执行以下操作:

    • 如果存在barrierCommand,则在当前线程中执行它。
    • 唤醒所有等待在屏障上的线程。
    • 重置count为0,并增加generation的值,以表示屏障已进入下一个循环。

以下是CyclicBarrierawait()方法的一个简化版源码分析(实际源码包含更多的错误处理和优化):

public int await() throws InterruptedException, BrokenBarrierException {
    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 {
                // not the last thread to arrive, wait until all others arrive
                if (!trip.await(this, timeout, unit))
                    throw new TimeoutException(); // not actually in real code, for simplicity
            } catch (InterruptedException ie) {
                if (g == generation && ! g.broken) {
                    breakBarrier();
                    throw ie;
                } else {
                    // Another thread must have interrupted us; we're about to notify them
                    // and if this was our interrupt, we'll throw it again below
                    Thread.currentThread().interrupt();
                }
            }

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

            if (g != generation)
                return index;

            // spinning wait for next generation
            Condition r = generation.register(count = parties - 1);
            // reset count to parties on each generation change
            // yield in case we're waiting for other threads
            while (count == parties - 1)
                Thread.yield(); // spin-wait
            // arrive at new generation
            r.signalAll();
        }
    } finally {
        lock.unlock();
    }
}

// Helper methods not shown for brevity: breakBarrier(), nextGeneration(), etc.
  • CyclicBarrier通过内部锁和条件变量来协调线程的等待和唤醒。
  • 当线程调用await()方法时,它会检查屏障的状态,并根据需要挂起或继续执行。
  • 如果所有线程都到达了屏障,则会执行可选的任务,并重置屏障以供下一轮使用。
  • 如果线程在等待过程中被中断或出现异常,则屏障可能会被标记为“破坏”状态,导致所有等待的线程都收到异常。

这种机制确保了线程之间的同步和协作能够以一种高效且可靠的方式进行。

二、CyclicBarrier的使用

2.1 CyclicBarrier使用场景

CyclicBarrier的使用场景非常广泛,特别是在需要将一个大任务拆分成多个小任务,并且这些小任务之间存在依赖关系的场景中。以下是一些具体的使用案例:

  1. 并行计算流水线:在并行计算中,常常需要将一个大任务拆分成多个阶段,每个阶段由一组线程完成。每个阶段都依赖于前一个阶段的结果。在这种情况下,可以使用CyclicBarrier来同步每个阶段的线程,确保它们都完成后再进入下一个阶段。
  2. 多线程测试:在进行多线程测试时,可能需要创建一组线程来模拟并发用户。为了确保所有线程都准备好后再开始测试,可以使用CyclicBarrier来同步它们的状态。
  3. 资源初始化:在某些情况下,可能需要一组线程共同完成某个资源的初始化工作。使用CyclicBarrier可以确保所有线程都完成初始化后再继续执行后续任务。

2.2 CyclicBarrier实现并行计算任务

下面代码中我们将模拟一个简单的并行计算任务,其中几个线程需要等待彼此完成后才能继续执行。

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {

    public static void main(String[] args) {
        // 设置屏障的阈值为3,意味着需要3个线程到达屏障后才会继续执行
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> {
            System.out.println("所有线程都已到达屏障,继续执行后续任务。");
        });

        // 创建并启动3个线程,每个线程将执行不同的任务并在到达屏障时等待其他线程
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 开始执行任务...");
                try {
                    // 模拟执行任务的时间
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 任务执行完毕,等待其他线程...");
                try {
                    // 到达屏障,等待其他线程
                    cyclicBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 通过屏障,可以继续执行后续任务...");
            }).start();
        }
    }
}
  • 我们创建了一个CyclicBarrier对象,设置其阈值为3,并提供了一个当所有线程到达屏障时执行的可选任务。
  • 然后我们创建了3个线程,每个线程都会执行一些任务,然后调用cyclicBarrier.await()方法到达屏障并等待其他线程。
  • 当所有3个线程都到达屏障时,屏障的操作将被执行,然后所有线程可以继续执行后续任务。

注意,由于线程调度的不确定性,每个线程打印的消息顺序可能会有所不同,但是你会看到“所有线程都已到达屏障,继续执行后续任务。”这条消息总是在所有线程都到达屏障后打印出来的。这证明了CyclicBarrier在协调多个线程同步点方面的作用。

三、CyclicBarrier与CountDownLatch的区别与联系

虽然CyclicBarrierCountDownLatch都是用于同步多个线程的工具类,但它们之间存在一些关键的区别和联系:

  1. 可重用性CyclicBarrier是可循环使用的。一旦所有线程通过屏障,它就会自动重置为初始状态,可以再次用于下一轮的线程同步。而CountDownLatch是一次性的,一旦计数器减到0,就不能再重用了。

  2. 计数方式CyclicBarrier的计数器是递增的,直到达到指定的线程数(阈值)。而CountDownLatch的计数器是递减的,每次调用countDown()方法都会使计数器减1。

  3. 使用场景:由于CyclicBarrier具有可重用性,它更适合于那些需要多次同步的场景,比如并行计算流水线或多次重复执行的多线程任务。而CountDownLatch则更适合于那些只需要一次同步的场景,比如等待一组线程完成初始化工作后再继续执行后续任务。

  4. 异常处理:当某个线程在等待过程中因为中断或异常而退出时,CyclicBarrierCountDownLatch的处理方式也有所不同。对于CyclicBarrier,所有等待在屏障点的线程都将收到一个BrokenBarrierException异常。而对于CountDownLatch,异常的处理取决于具体的实现和调用方式(比如是否使用了await(long timeout, TimeUnit unit)方法)。

四、总结

CyclicBarrier是Java并发包中提供的一个强大且灵活的同步工具类。它允许一组线程在一个公共的屏障点上互相等待,直到所有线程都到达这个点后再继续执行后续任务。这使得它在处理复杂的多线程同步问题时非常有用。通过深入理解CyclicBarrier的内部机制和使用场景,我们可以更好地利用它来编写高效、可靠且易于维护的并发程序。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GPT模型支持下的Python-GEE遥感云大数据分析、管理与可视化技术及多领域案例应用

随着航空、航天、近地空间等多个遥感平台的不断发展&#xff0c;近年来遥感技术突飞猛进。由此&#xff0c;遥感数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量也大幅增长&#xff0c;使其越来越具有大数据特征。对于相关研究而言&#xff0c;遥感大数据的出现为其提…

ubuntu 如何使用阿里云盘

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

算法---二分查找练习-2(寻找旋转排序数组中的最小值)

寻找旋转排序数组中的最小值 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;点这里 2. 讲解算法原理 首先&#xff0c;检查数组的最后一个元素是否大于第一个元素。如果是&#xff0c;说明数组没有进行旋转&#xff0c;直接返回第一个元素作为最小值…

yolo中RANK、LOACL_RANK以及WORLD_SIZE的介绍

在YOLO系列算法的分布式训练中&#xff0c;"rank"、"local-rank" 和 "world_size" 是三个相关的概念&#xff0c;它们在协调和管理分布式训练过程中起着关键作用。 1. 名词解释 Rank&#xff08;排名&#xff09;&#xff1a;在分布式训练中&…

Django单表数据库操作

单表操作 测试脚本 当你只想测试django某一个py文件的内容,可以不用书写前后端的交互,直接写一个测试脚本即可 单表删除 数据库操作方法: 1.all():查询所有的数据 2.filter():带有过滤条件的查询 3.get():直接拿数据对象,不存在则报错 4.first():拿queryset里面的第一个元素…

个人商城系统开源(配置支付宝支付2)

原文地址&#xff1a;个人商城系统开源&#xff08;配置支付宝支付2&#xff09; - Pleasure的博客 下面是正文内容&#xff1a; 前言 在上一篇文章中我曾提到过关于网站支付宝支付的方法&#xff0c;接下来我们来介绍第二种。 个人博客地址&#xff1a;个人商城系统开源&…

xinference - 大模型分布式推理框架

文章目录 关于 xinference使用1、启动 xinference设置其他参数 2、加载模型3、模型交互 其它报错处理 - transformer.wte.weight 关于 xinference Xorbits Inference&#xff08;Xinference&#xff09;是一个性能强大且功能全面的分布式推理框架。 可用于大语言模型&#xff…

【Flask开发实战】配置python虚拟环境

python 虚拟环境是一种管理 Python 项目依赖的工具&#xff0c;它可以帮助你在不同的项目中使用不同的 Python 版本和库&#xff0c;避免了不同项目之间依赖冲突的问题。虚拟环境相当于一个抽屉&#xff0c;在这个抽屉中安装的任何软件包都不会影响到其他抽屉。并且在项目中&am…

线上教学平台|基于Spring Boot+ Mysql+Java+ B/S结构的线上教学平台设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

MapReduce框架原理

目录 前言一、InputFormat数据输入1.1 切片与MapTask并行度决定机制1.1.1 问题引出1.1.2 MapTask并行度决定机制1.1.3 数据切片与MapTask并行度决定机制 1.2 FileInputFormat切片机制1.2.1 切片大小参数配置1.2.2 切片机制 1.3 TextInputFormat1.3.1 FileInputFormat实现类1.3.…

ASPICE规范之系统追溯矩阵

系统追溯矩阵的需求来自 ISO26262 举例在描述系统追溯矩阵时&#xff1a;客户需求->系统需求&#xff1b;系统需求->客户需求&#xff1b;系统需求->软件需求&#xff1b;系统需求->硬件需求

Ollama 运行 Cohere 的 command-r 模型

Ollama 运行 Cohere 的 command-r 模型 0. 引言1. 安装 MSYS22. 安装 Golang3. Build Ollama4. 运行 command-r 0. 引言 Command-R Command-R 是一种大型语言模型&#xff0c;针对对话交互和长上下文任务进行了优化。它针对的是“可扩展”类别的模型&#xff0c;这些模型在高…

(简单成功)Mac:命令设置别名

案例&#xff1a;给"ls -l"命令&#xff0c;设置别名通过”ll“快速访问 1、在项目根目录底下查看有无.bash_profile文件&#xff0c;注意这个是个隐藏文件&#xff0c;需要使用ls -a命令查看&#xff1a; 没有.bash_profile新建一个文件&#xff0c; 在最后添加一行…

CMake笔记之GLOB和GLOB_RECURSE的使用方法

CMake笔记之GLOB和GLOB_RECURSE的使用方法 —— 杭州 2024-03-19 夜 文章目录 CMake笔记之GLOB和GLOB_RECURSE的使用方法1.GLOB使用方法2.GLOB对比GLOB_RECURSE 1.GLOB使用方法 在 CMake 中&#xff0c;file(GLOB ...) 命令用于将匹配特定模式的文件列表赋值给变量。这可以用…

HarmonyOS应用开发者高级认证答案

** HarmonyOS应用开发者高级认证 ** 以下是高级认证答案&#xff0c;存在个别选项随机顺序答案&#xff0c;自行辨别 判断题 云函数打包完成后&#xff0c;需要到 AppGallery Connect 创建对应函数的触发器才可以在端侧中调用 错 在 column 和 Row 容器组件中&#xff0c;a…

HighTec_TC4 编译器移植 Aurix ADS

ADS 是英飞凌推出的针对 AURIX 芯片的开发平台&#xff0c;该开发环境基于业内流行的 Eclipse 打造而成。 HighTec 作为英飞凌的全球重要合作伙伴和 PDH&#xff0c;作为专业的编译器供应商和嵌入式产品方案提供商&#xff0c;HighTec 早已经为英飞凌最新一代 AURIX TC4XX 芯片…

LeetCode每日一题 翻转二叉树(二叉树)

题目描述 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 示例 2&#xff1a; 输入&#xff1a;root [2,1,3] 输出&#xff1a;[2,3,1]示…

Vmware安装Kali

镜像下载地址&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/kali-images/kali-2023.3/kali-linux-2023.3-installer-amd64.iso 新建虚拟机&#xff1a; 新建虚拟机--典型--稍后安装操作系统--Linux--Debian 10.X 64 位&#xff08;因为kali是基于debian开发的&#xff0…

软件推动开放自动化落地

当你唯一拥有的是一把锤子时&#xff0c;你周围的一切都是钉子。 软件是硬件设备的护城河&#xff0c;国际自动化厂商不遗余力地开发各种新型工业软件&#xff0c;其战略站在应用的制高点。以前我们追求硬件兼容&#xff0c;现在我们要致力于应用引领。如果我们拥有强大的SCADA…

基于python高校选课系统设计与实现flask-django-nodejs-php

随着互联网技术的不断发展&#xff0c;高校选课系统的建设和应用已成为当前高校教育改革的重要方向。选课系统作为高校教务管理的重要组成部分&#xff0c;对于提高教学质量、提高学生的学习效率、优化教学资源配置具有重要的意义。本论文旨在探讨高校选课系统的设计与实现。随…