深入理解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.rhkb.cn/news/279555.html

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

相关文章

三种简单方法教你手机mp4怎么转换成mp3!

在日常生活中&#xff0c;我们经常会遇到想要将手机上的MP4视频转换为MP3音频的情况。可能是想要提取音频内容&#xff0c;例如歌曲或录音&#xff0c;方便在其他设备上播放或分享&#xff1b;也可能是为了节省手机存储空间&#xff0c;将视频文件转换为更小的音频文件。 无论…

【HTTP完全注解】看了还搞不懂缓存你直接来打我

HTTP缓存 HTTP缓存是一种HTTP的性能优化机制&#xff0c;它是为了提高Web页面加载速度和减轻服务器负载而设计的&#xff0c;通过这种机制&#xff0c;Web浏览器或其他客户端可以存储先前获取的Web资源的副本&#xff0c;并在后续请求相同资源时使用这些副本&#xff0c;而不是…

Java学习笔记21——使用JDBC访问MySQL数据库

JDBC&#xff08;Java Database Connectivity&#xff0c;Java数据库连接&#xff09;是应用程序编程借口&#xff08;API&#xff09;&#xff0c;描述了一套访问关系数据库的标准Java类库。可以在程序中使用这些API&#xff0c;连接到关系数据库&#xff0c;执行SQL语句&…

【技术栈】Redis 中的事务及持久化方式

SueWakeup 个人主页&#xff1a; SueWakeup 系列专栏&#xff1a;学习技术栈 个性签名&#xff1a;保留赤子之心也许是种幸运吧 本文封面由 凯楠&#x1f4f8; 友情提供 目录 相关传送门 1. Redis 中的事务 2. Redis 持久化 2.1 RDB 方式 2.1.1 RDB手动 2.1.2 RDB自动 2.…

跨越时空的纽带:探索Facebook如何连接人与人

引言 Facebook作为全球最大的社交媒体平台之一&#xff0c;已经成为了人们日常生活中不可或缺的一部分。它不仅仅是一个社交网络&#xff0c;更是连接人与人、人与世界的纽带。在这篇文章中&#xff0c;我们将深入探讨Facebook如何跨越时空&#xff0c;连接人与人之间的关系&a…

Flutter 初始WidgetState 简单应用案例分析

本系列文章主要整理Flutter的知识汇总&#xff0c;由浅入深&#xff0c;从Widget的搭建到其中的原理。本文还是围绕Widget在开发中应用和理解。 关于Flutter环境配置和首次创建可以参考前面文章。链接如下&#xff1a; Flutter 安装部署与认识Dart语言 Flutter 使用AndroidS…

Spring Cloud 整合 GateWay

目录 第一章 微服务架构图 第二章 Spring Cloud整合Nacos集群 第三章 Spring Cloud GateWay 第四章 Spring Cloud Alibaba 整合Sentinel 第五章 Spring Cloud Alibaba 整合SkyWalking链路跟踪 第六章 Spring Cloud Alibaba 整合Seata分布式事务 第七章 Spring Cloud 集成Auth用…

多模匹配算法AC算法和单模匹配算法BM

多模匹配算法之AC算法详解 算法概述  Aho-Corasick算法 - 这是一种字典匹配算法,它用于在输入文本中查找字典中的字符串。时间复杂度是线性的。该算法应用有限自动机巧妙地将字符比较转化为了状态转移。  该算法的基本思想 − 在预处理阶段,AC自动机算法建立…

Springboot通过注解+切面实现接口权限校验

Springboot通过注解&#xff0b;切面实现接口权限校验 主要说一下在对接口请求时&#xff0c;如何用注解切面去拦截校验当前登录用户是否有访问权限 1.首先创建注解 HasPermission &#xff0c;跟普通注解创建方式基本一致 Retention(RetentionPolicy.RUNTIME) Target(Element…

小火星露谷管理器 报错:“你似乎没有安装Edge的webview2”

错误 解决办法 你可以到这个地方下载安装webview2 https://developer.microsoft.com/zh-cn/microsoft-edge/webview2/?formMT00IS

2024年亚洲图像处理趋势会议(ATIP 2024)即将召开!

2024年亚洲图像处理趋势会议&#xff08;简称&#xff1a;ATIP 2024&#xff09;将于2024年6月21日至23日在英国伦敦举行。在会议上我们将与相关领域的研究人员和知名专业人士共同讨论关于图像处理学科的最新研究方向及进展&#xff0c;评估当前最先进的技术和未来研究的关键领…

Tomcat(Win+Linux)安装教程

Windows环境安装 Tomcat安装及配置教程主要分为四步&#xff1a; 步骤一&#xff1a;确认自己是否已 安装JDK&#x1f50d; 步骤二&#xff1a;下载安装Tomcat 步骤三&#xff1a;Tomcat配置环境变量 步骤四&#xff1a;验证Tomcat配置是否成功 OK&#xff0c;我们开始&#x…

数据库基本介绍及编译安装mysql

目录 数据库介绍 数据库类型 数据库管理系统&#xff08;DBMS&#xff09; 数据库系统 DBMS的工作模式 关系型数据库的优缺点 编译安装mysql 数据库介绍 数据&#xff1a;描述事物的的符号纪录称为数据&#xff08;Data&#xff09; 表&#xff1a;以行和列的形式组成…

python大学生健身爱好者交流网站flask-django-nodejs-php

任何系统都要遵循系统设计的基本流程&#xff0c;本系统也不例外&#xff0c;同样需要经过市场调研&#xff0c;需求分析&#xff0c;概要设计&#xff0c;详细设计&#xff0c;编码&#xff0c;测试这些步骤&#xff0c;基于python技术、django/flask框架、B/S机构、Mysql数据…

【No.13】蓝桥杯二分查找|整数二分|实数二分|跳石头|M次方根|分巧克力(C++)

二分查找算法 知识点 二分查找原理讲解在单调递增序列 a 中查找 x 或 x 的后继在单调递增序列 a 中查找 x 或 x 的前驱 二分查找算法讲解 枚举查找即顺序查找&#xff0c; 实现原理是逐个比较数组 a[0:n-1] 中的元素&#xff0c;直到找到元素 x 或搜索整个数组后确定 x 不在…

linux网络服务学习(1):nfs

1.什么是nfs NFS&#xff1a;网络文件系统。 *让客户端通过网络访问服务器磁盘中的数据&#xff0c;是一种在linux系统间磁盘文件共享的方法。 *nfs客户端可以把远端nfs服务器的目录挂载到本地。 *nfs服务器一般用来共享视频、图片等静态数据。一般是作为被读取的对象&…

国内git最新版本下载链接2.44

git官网地址:Git - Downloading Package (git-scm.com) 蓝奏云: ​​​​​​gGit-2.44.0-64-bit.exe - 蓝奏云 git仓库地址:git/git: Git Source Code Mirror - This is a publish-only repository but pull requests can be turned into patches to the mailing list via …

算法笔记p251队列循环队列

目录 队列循环队列循环队列的定义初始化判空判满入队出队获取队列内元素的个数取队首元素取队尾元素 队列 队列是一种先进先出的数据结构&#xff0c;总是从队尾加入元素&#xff0c;从队首移除元素&#xff0c;满足先进先出的原则。队列的常用操作包括获取队列内元素的个数&a…

Typecho博客后台登陆界面美化

登录界面&#xff1a; 食用方法&#xff1a; 备份 admin 目录 压缩包内容上传到 admin 目录内。 结构:网站根目录 /admin/login.php 结构:网站根目录 /admin/style 修改 login.php 第35行&#xff0c;把“季春二九管理后台”替换成自己的信息 清理缓存&#xff0c;开始体验新的…

释放创造力,Nik Collection 6 by DxO 点亮你的视觉世界

在数字摄影时代&#xff0c;后期处理是提升摄影作品品质的重要环节。而Nik Collection 6 by DxO作为一套优秀的滤镜插件套装&#xff0c;不仅为摄影师提供了丰富的后期处理工具&#xff0c;更让他们能够释放无限的创造力&#xff0c;打造出惊艳的视觉作品。 Nik Collection 6 …