深入理解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) {  // trippedboolean 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 outfor (;;) {try {// not the last thread to arrive, wait until all others arriveif (!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 belowThread.currentThread().interrupt();}}if (g.broken)throw new BrokenBarrierException();if (g != generation)return index;// spinning wait for next generationCondition r = generation.register(count = parties - 1);// reset count to parties on each generation change// yield in case we're waiting for other threadswhile (count == parties - 1)Thread.yield(); // spin-wait// arrive at new generationr.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的内部机制和使用场景,我们可以更好地利用它来编写高效、可靠且易于维护的并发程序。

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

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

相关文章

数据分析-Pandas序列滑动窗口配置参数

数据分析-Pandas序列滑动窗口配置参数 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表&…

初探Springboot 参数校验

文章目录 前言Bean Validation注解 实践出真知异常处理 总结 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 前言 工作中我们经常会遇到验证字段是否必填&#xff0c;或者字段的值是否…

极客早报第3期:罗斯否认插足凯特王妃婚姻;清明放假调休3天;国产伟哥去年销售近13亿

一分钟速览新闻点&#xff01; 每日简报 罗斯否认插足凯特王妃婚姻清明放假调休3天国产伟哥去年销售近13亿男子持台球杆殴打2名女店员被抓今日春分淀粉肠小王子带货日销售额涨超10倍[高中生被打伤下体休学 邯郸通报](https://www.baidu.com/s?wd高中生被打伤下体休学 邯郸通报…

ARM Cortex-R82处理器在压缩SSD场景的应用

ScaleFlux公司宣布在其下一代企业级SSD控制器产品线中采用Arm公司的Cortex-R82处理器。这一决策旨在应对企业环境中对高带宽存储解决方案日益增长的需求&#xff0c;并通过提升数据传输速度和效率来满足市场期待。 Arm Cortex-R82处理器是Arm公司迄今为止性能最强的实时处理器…

STC 51单片机烧录程序遇到一直检测单片机的问题

准备工作 一&#xff0c;需要一个USB-TTL的下载器 &#xff0c;并安装好对应的驱动程序 二、对应的下载软件&#xff0c;stc软件需要官方的软件&#xff08;最好是最新的&#xff0c;个人遇到旧的下载软件出现问题&#xff09; 几种出现一直检测的原因 下载软件图标&#xf…

Unbuntu20.04 git push和pull相关问题

文章目录 Unbuntu20.04 git push和pull使用&#xff11;&#xff0e;下载[Git工具包](https://git-scm.com/downloads)&#xff12;&#xff0e;建立本地仓库&#xff13;&#xff0e;将本地仓库与github远程仓库关联&#xff14;&#xff0e;将本地仓库文件上传到github远程仓…

J4G企业通讯ip电话 sip对讲主机 停车场对讲主机

J4G企业通讯ip电话 sip对讲主机 停车场对讲主机 SV-J4G 是一款企业级彩屏网络电话&#xff0c;具有高清语音&#xff0c;320x240 2.8英寸彩屏&#xff0c;支持千兆以太网&#xff0c;12个SIP账号&#xff0c;支持PoE供电&#xff0c;支持外接EHS无线耳机&#xff0c;三方电话会…

Grok-1:参数量最大的开源大语言模型

Grok-1&#xff1a;参数量最大的开源大语言模型 项目简介 由马斯克领衔的大型模型企业 xAI 正式公布了一项重要动作&#xff1a;开源了一个拥有 3140 亿参数的混合专家模型&#xff08;MoE&#xff09;「Grok-1」&#xff0c;连同其模型权重和网络架构一并公开。 此举将 Gro…

清华大模型ChatGLM3部署初体验

正文共&#xff1a;1555 字 17 图&#xff0c;预估阅读时间&#xff1a;2 分钟 ChatGLM3是智谱AI和清华大学KEG实验室联合发布的对话预训练模型。该项目在GitHub的工程链接为&#xff1a; https://github.com/THUDM/ChatGLM3 在人工智能领域中&#xff0c;类似“3B”、“6B”、…

C++--STL标准库

一.模板 模板是C中泛型编程的基础。一个模板就是一个创建类或函数的蓝图。 生活中常见的模板有: 编写一个比较两个值大小的函数&#xff0c;如果第一个值大于第二个值返回大于0的数字,两个值相等返回0,第一个值小于第二个值返回小于0的数字。 我们可以根据值类型定义多个函数&…

Go语言实战:深入掌握标准库flag的强大用法

Go语言实战&#xff1a;深入掌握标准库flag的强大用法 引言flag库基础命令行参数的基本概念使用flag库定义和解析命令行参数处理非选项命令行参数小结 高级用法自定义Flag的解析命令行参数的分组和嵌套小结 实战技巧组织复杂命令行应用的参数错误处理和用户帮助信息调试命令行应…

php基于人工智能预警突发疾病系统python-flask-django-nodejs

根据现实需要&#xff0c;此系统我们设计出一下功能&#xff0c;主要有以下功能模板。 前台功能&#xff1a;首页、医生、疾病知识、后台管理。 医生功能&#xff1a;首页、个人中心、咨询信息管理、疾病预警管理、高血压管理、糖尿病管理。 用户功能&#xff1a;首页、个人中心…

数据分析能力模型分析与展示

具体内容&#xff1a; 专业素质 专业素质-01 数据处理 能力定义•能通过各种数据处理工具及数据处理方法&#xff0c;对内外部海量数据进行清洗和运用&#xff0c;提供统一数据标准&#xff0c;为业务分析做好数据支持工作。 L1•掌握一…

SinoDB客户端工具dbaccess

类似Oracle的客户端工具sqlplus&#xff0c;Mysql的客户端工具mysql&#xff0c;SinoDB数据库也有自带的命令行客户端工具dbaccess。 dbaccess 识别用户输入&#xff0c;将用户输入的 SQL 语句打包发送给 SinoDB 数据库服务器执行&#xff0c;然后接收服务器的执行结果&#xf…

Leet code 238 除自身以外的数组的乘积

解题思路 以示例1为例 创建两个数组dp&#xff08;统计该位置之的所有乘积&#xff09; bp&#xff08;统计该位置之后的所有乘积&#xff09; 比如 1 2 3 4 3的dp应该等于 1*2 bp应该等于 4 这样 dp* bp就为该位置的答案 分别计算出每个位置前后的乘积然后放入数组 然…

3.leetcode---验证回文串(Java版)

链接: https://leetcode.cn/problems/XltzEq/description/ 给定一个字符串 s &#xff0c;验证 s 是否是 回文串 &#xff0c;只考虑字母和数字字符&#xff0c;可以忽略字母的大小写。 本题中&#xff0c;将空字符串定义为有效的 回文串 。 示例 1: 输入: s “A man, a plan…

2022年安徽省职业院校技能大赛 (高职组)“云计算”赛项样卷

#需要资源或有问题的&#xff0c;可私博主&#xff01;&#xff01;&#xff01; #需要资源或有问题的&#xff0c;可私博主&#xff01;&#xff01;&#xff01; #需要资源或有问题的&#xff0c;可私博主&#xff01;&#xff01;&#xff01; 第一场次&#xff1a;私有云(5…

【Linux】自动化构建工具-make/Makefile

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 前言2. 认识make/Makefile3. 了解make/Makefile原理3.1 依赖关系和依赖方法3.2 make检测的顺序3.3 PHONY:XXX 4. makefile内置符号 1. 前言 在上一篇中已经了解了【Linux】编译器-gcc/g使用&#xff0c;这次来一起…

具有功耗低、触控灵敏度高、抗干扰能力强等功能的单键电容式触控芯片——TS223B,适用于小家电、电子玩具等产品

•应用领域• 适用于小家电、电子玩具、智能物联网等各种触控产品方案。 •功能介绍• 这款推出的单键电容式触控芯片TS223B具有功耗低、触控灵敏度高、抗干扰能力强等众多优势&#xff0c;输出方式包括直接输出、电平翻转输出&#xff0c;并且输出的初始状态可以配置&#xff…

Outlook邮箱后缀是什么?如何改邮箱后缀?

Outlook邮箱后缀可以更改吗&#xff1f;微软有哪些后缀的邮箱&#xff1f; 对于许多刚接触Outlook邮箱的新手来说&#xff0c;了解Outlook邮箱后缀是必不可少的一步。那么&#xff0c;Outlook邮箱后缀究竟是什么呢&#xff1f;接下来&#xff0c;AokSend就来详细探讨一下这个问…