CyclicBarrier线程同步

关于作者: CSDN内容合伙人、技术专家, 从零开始做日活千万级APP,带领团队单日营收超千万。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业化变现、人工智能等,希望大家多多支持。

目录

  • 一、导读
  • 二、概览
      • CyclicBarrier 和 countdownlatch 的区别
  • 三、使用
  • 四、原理
  • 五、 推荐阅读

ddd

一、导读

我们继续总结学习Java基础知识,温故知新。

本文涉及知识点:
AQS - AbstractQueuedSynchronizer
CAS(Compare And Swap)
锁概念 volatile
ReentrantLock

二、概览

一、是什么
CyclicBarrier是JDK提供的一个同步工具,它的作用是让一组线程全部达到一个状态之后再全部同时执行。
特点:
所有线程执行完毕之后是可以重用的。

二、实现原理
CyclicBarrier就是利用了 AQS - AbstractQueuedSynchronizer 的共享锁来实现。
调用await的线程会被阻塞,直到计数器为0,在继续执行。
内部维护parties记录总线程数,count用于计数,最开始count=parties,调用await()之后count原子递减,当count为0之后,再次将parties赋值给count,这就是复用的原理

三、使用场景
一组线程全部达到一个状态之后再全部同时执行。
eg:
多线程计算结果,最后在合并结果。

四、优劣

  • 优点 :代码优雅,不需要对线程池进行操作,将线程池作为 Bean 的情况下有很好的使用场景。
  • 缺点 :需要提前知道线程数量;性能确实,呃呃呃呃呃,差了点。哦对了,还需要在线程代码块内加上异常判断,否则在 countDown 之前发生异常而没有处理,就会导致主线程永远阻塞在 await。
    需要提前知道数量。

需要注意异常的处理,await() 跟 num 必须成对出现,否则线程会一直阻塞。

CyclicBarrier 和 countdownlatch 的区别

CyclicBarrier和CountDownLatch都是多线程同步工具,但是它们的工作机制和用途略有不同:
CyclicBarrier更适合于需要循环等待多个线程的场景,而CountDownLatch则更适合于需要等待特定数量线程完成的场景。

  • 构造和计数机制
    CountDownLatch在构造时需要一个初始计数值,每调用一次countDown()方法,计数器减1,当计数器达到0时,await()方法会释放等待线程。CyclicBarrier则不需要初始计数值,它通过在每个线程中设置一个屏障(barrier)来阻塞线程,直到所有线程都到达屏障后才会释放。

  • 线程状态
    CountDownLatch不会改变线程状态,只是让线程等待,而CyclicBarrier在屏障未达到时阻塞线程,达到屏障后才解除阻塞。

  • 重用性
    CyclicBarrier是可重用的,在所有线程都通过屏障后,可以再次设置新的屏障,而CountDownLatch是一次性的。

  • 等待线程数
    CountDownLatch等待的线程数是固定的,而CyclicBarrier可以等待任意数量的线程

countdownlatch 是一个线程等待其他线程执行完毕后再执行,
CyclicBarrier 是每一个线程等待所有线程执行完毕后,再执行

三、使用

从字面上的意思可以知道,这个类的中文意思是“循环栅栏”。大概的意思就是一个可循环利用的屏障。
它的作用就是会让所有线程都等待完成后才会继续下一步行动。
现实生活中我们经常会遇到这样的情景,在进行某个活动前需要等待人全部都齐了才开始。例如吃饭时要等全家人都上座了才动筷子,旅游时要等全部人都到齐了才出发,比赛时要等运动员都上场后才开始。

CyclicBarrier叫做回环屏障,它的作用是让一组线程全部达到一个状态之后再全部同时执行,而且他有一个特点就是所有线程执行完毕之后是可以重用的。

public class CyclicBarrierTest {private static int num = 3;private static CyclicBarrier cyclicBarrier = new CyclicBarrier(num, () -> {System.out.println("---------所有人都好了, await 次数到 ----------");});private static ExecutorService executorService = Executors.newFixedThreadPool(num);public static void main(String[] args) throws Exception{test2Cyclic();
//        test2Cyclic1();executorService.shutdown();}public static void test2Cyclic1() {executorService.submit(() -> {System.out.println("A在上厕所");try {Thread.sleep(4000);System.out.println("A上完了");cyclicBarrier.await();System.out.println("会议结束,A退出");} catch (Exception e) {e.printStackTrace();}finally {}});executorService.submit(()->{System.out.println("B在上厕所");try {Thread.sleep(2000);System.out.println("B上完了");cyclicBarrier.await();System.out.println("会议结束,B退出");} catch (Exception e) {e.printStackTrace();}finally {}});executorService.submit(()->{System.out.println("C在上厕所");try {Thread.sleep(3000);System.out.println("C上完了");cyclicBarrier.await();System.out.println("会议结束,C退出");} catch (Exception e) {e.printStackTrace();}finally {}});}public static void test2Cyclic() {executorService.submit(() -> {System.out.println("A在上厕所");try {Thread.sleep(4000);System.out.println("A上完了");cyclicBarrier.await();System.out.println("会议结束,A退出,开始撸代码");cyclicBarrier.await();System.out.println("C工作结束,下班回家");cyclicBarrier.await();} catch (Exception e) {e.printStackTrace();} finally {}});executorService.submit(() -> {System.out.println("B在上厕所");try {Thread.sleep(2000);System.out.println("B上完了");cyclicBarrier.await();System.out.println("会议结束,B退出,开始摸鱼");cyclicBarrier.await();System.out.println("B摸鱼结束,下班回家");cyclicBarrier.await();} catch (Exception e) {e.printStackTrace();} finally {}});executorService.submit(() -> {System.out.println("C在上厕所");try {Thread.sleep(3000);System.out.println("C上完了");cyclicBarrier.await();System.out.println("会议结束,C退出,开始摸鱼");cyclicBarrier.await();System.out.println("C摸鱼结束,下班回家");cyclicBarrier.await();} catch (Exception e) {e.printStackTrace();} finally {}});executorService.shutdown();}
}

四、原理

子类的任务有:

  1. 通过CAS操作维护共享变量state。
  2. 重写资源的获取方式。
  3. 重写资源释放的方式。

在这里插入图片描述

一起看下代码

// 1、 创建CountDownLatch并设置计数器值,count代表计数器个数(内部是共享锁,本质就是上了几次锁)
第二个构造方法有一个 Runnable 参数,这个参数的意思是最后一个到达线程要做的任务
public CyclicBarrier(int parties, Runnable barrierAction) // 2、子线程调用await()方法时,获取独占锁,同时对count递减,进入阻塞队列,然后释放锁
// 当第一个线程被阻塞同时释放锁之后,其他子线程竞争获取锁,操作同1
public void await()// 等待一定时间
public void await(long timeout, TimeUnit unit)//同步锁private final ReentrantLock lock = new ReentrantLock();//条件队列private final Condition trip = lock.newCondition();

CyclicBarrier借助ReentranLock与Condition来对线程进行阻塞的

private int dowait(boolean timed, long nanos)throws InterruptedException, BrokenBarrierException,TimeoutException {final ReentrantLock lock = this.lock;lock.lock();try {//当前代final Generation g = generation;//判断当前代的状态,如果当前代后的屏障被打破,则g.broken返回true,否则返回false。if (g.broken)throw new BrokenBarrierException();//判断当前线程是否被中断if (Thread.interrupted()) {//如果当前线程已经被中断,则调用breakBarrier()//该方法代码为generation.broken = true;count = parties;trip.signalAll();//可见,只做了3件事:先将当前代的屏障变为打破状态,接着重置计数器的值,最后唤醒所有被阻塞的线程breakBarrier();//最后抛出中断异常throw new InterruptedException();}//将计数器的值减1int index = --count;if (index == 0) {//如果当前计数器的值为0boolean ranAction = false;try {//则先执行换代任务,可以看得出来,是由最后一个到达屏障的线程执行的final Runnable command = barrierCommand;if (command != null)command.run();ranAction = true;//开启下一代,这个方法的代码为trip.signalAll();count = parties;generation = new Generation();//该代码唤醒所有被阻塞的线程,重置计数器的值,并且实例化下一代nextGeneration();return 0;} finally {//如果换代任务未执行成功,则先将当前代的屏障变为打破状态,接着重置计数器的值,最后唤醒所有被阻塞的线程if (!ranAction)breakBarrier();}}//当前线程一直阻塞,直到“有parties个线程到达barrier” 或 “当前线程被中断” 或 “超时”这3者之一发生//死循环for (;;) {try {if (!timed)//如果不是定时等待,则调用条件队列的await()进行阻塞trip.await();else if (nanos > 0L)//如果是定时等待,则调用条件队列的awaitNanos进行等待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();//如果是正常换代,则返回index值if (g != generation)return index;//如果是定时等待,且时间已经到了,则打破屏障,唤醒所有阻塞的线程,最后抛出异常if (timed && nanos <= 0L) {breakBarrier();throw new TimeoutException();}}} finally {lock.unlock();}}
  1. 当子线程调用await()方法时,获取独占锁,同时对count递减,进入阻塞队列,然后释放锁
  2. 当第一个线程被阻塞同时释放锁之后,其他子线程竞争获取锁,操作同1
  3. 直到最后count为0,执行CyclicBarrier构造函数中的任务,执行完毕之后子线程继续向下执行

五、 推荐阅读

Java 专栏

SQL 专栏

数据结构与算法

Android学习专栏

在这里插入图片描述

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

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

相关文章

软考系统架构设计师考试冲刺攻略

系统架构冲刺攻略 上篇为综合知识&#xff0c;介绍了系统架构设计师应熟练掌握的基本知识&#xff0c;主要包括绪论、计算机系统、信息系统、信息安全技术、软件工程、数据库设计、系统架构设计、系统质量属性与架构评估、软件可靠性、软件架构的演化和维护、未来信息综合技术等…

PHPEXCEL解决行数超过65536不显示问题

起因自然是导出数据到excel文件时&#xff0c;数据缺少现象。 百度讲解是将xls文件另存为xlsx文件。 除了这里的原因&#xff0c;还有一点是phpExcel存在两个写入类PHPExcel_Writer_Excel2007和PHPExcel_Writer_Excel5&#xff0c;而只有PHPExcel_Writer_Excel2007支持超过65…

NLP Bi-Encoder和Re-ranker

Retrieve & Re-Rank https://www.sbert.net/examples/applications/retrieve_rerank/README.html Bi-Encoder vs. Cross-Encoder https://www.sbert.net/examples/applications/cross-encoder/README.html Bi-Encoder会用BERT对输入文本编码&#xff0c;再根据cosine相似度…

Autosar诊断实战系列25-UDS 0x27服务相关问题思考

本文框架 前言0x27服务几个相关问题1. 安全访问种子的随机数能不能是全0?2. 安全级别之间是否有联系?是怎么确定的?3. 安全访问错误计数器具体变化策略?前言 在本系列笔者将结合工作中对诊断实战部分的应用经验进一步介绍常用UDS服务的进一步探讨及开发中注意事项, Dem/D…

华为云HECS服务器下docker可视化(portainer)

一、docker安装 华为云HECS安装docker-CSDN博客 二、portainer安装 portainer地址&#xff1a;Portainer: Docker and Kubernetes Management Platform 当前portainer分CE&#xff08;开源版&#xff09; 和 BE&#xff08;商业版&#xff09;&#xff0c;用CE即可 1 创建…

RK3568驱动指南|第七期-设备树-第58章 实例分析:时钟

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

类的继承简介

一、声明格式&#xff1a; class 子类名&#xff1a;继承方式(public private protected) 父类名{子类成员表} 二、继承过程&#xff1a; 吸取父类成员——>改造父类成员——>添加新成员 三、作用&#xff1a; 子类会继承父类中的方法(不包括构造和析构函数)与属性 …

stable diffusion如何解决gradio外链无法开启的问题

问题确认 为了确认gradio开启不了是gradio库的问题还是stable diffusion的问题&#xff0c;可以先执行这样一段demo代码 import gradio as grdef greet(name):return "Hello " name "!"demo gr.Interface(fngreet, inputs"text", outputs&q…

Vue生命周期钩子

vue生命周期表示在组件创建后的一系列变化&#xff0c;其中钩子函数会在生命周期的关键节点中被调用 为什么在beforeCreated()时&#xff0c;data和methods方法还没有创建&#xff0c;但是在beforeCreated()里面打印this可以看到data相关的数据&#xff1f; 跟浏览器有关&…

FL Studio中文最新21破解版本水果软件下载

那么&#xff0c;大家知道编曲是什么吗&#xff1f;编曲和作曲又有什么区别呢&#xff1f; 一首歌的制作过程通常是由作词或作曲开始的&#xff0c;作曲就是运用基本乐理、和声学、复调、配器法、曲式结构的技术理论体系来表达创作者音乐思想的方法。说白了其实就是制作一首歌…

LeetCode13——罗马数字转整数

解题思想&#xff1a; 前后指针 左边比右边小 做减法 左边比右边大 做加法 最后一个数字直接加。 package keepcoding.leetcode.leetcode13;public class Result02 {public static void main(String[] args) {int result romanToInt("XIV");System.out.println(re…

设计模式:模板模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)

简介&#xff1a; 模板模式&#xff0c;它是一种行为型设计模式&#xff0c;它定义了一个操作中的算法的框架&#xff0c;将一些步骤延迟到子类中实现&#xff0c;使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 通俗地说&#xff0c;模板模式就是将某一行…

C++初阶(五)类和对象

文章目录 一、C两大类型二、类的6个默认成员函数三、构造函数1、概念2、特性1、构造函数自动调用特性演示2、无参有参调用两种情况演示3、函数重载演示4、默认构造函数组成及演示5、内置类型成员不初始化的补丁演示 3、析构函数1、概念2、特性1、代码演示2、析构两种情况 4、构…

vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法

vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法 先看一下效果图&#xff08;想在表单里动态的增删 form-item&#xff0c;然后添加rules&#xff0c;校验其必填项&#xff1b; &#xff09;: html部分 <div v-for"(item, index) in …

GitHub下载太慢的解决方案

修改hosts文件&#xff1a; windows的hosts文件在 C:\Windows\System32\drivers\etc\hosts cmd管理员运行命令notepad C:\Windows\System32\drivers\etc\hosts 然后cmd命令重启网络ipconfig /flushdns windows修改hosts Ubuntu22.04修改hosts sudo vim /etc/hosts # This fil…

基于北方苍鹰优化的BP神经网络(分类应用) - 附代码

基于北方苍鹰优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于北方苍鹰优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.北方苍鹰优化BP神经网络3.1 BP神经网络参数设置3.2 北方苍鹰算法应用 4.测试结果…

13 Multi-Head Self-Attention(从空间角度解释为什么做多头)

博客配套视频链接: https://space.bilibili.com/383551518?spm_id_from=333.1007.0.0 b 站直接看 配套 github 链接:https://github.com/nickchen121/Pre-training-language-model 配套博客链接:https://www.cnblogs.com/nickchen121/p/15105048.html 上节课回顾 0:40 At…

系统架构之微服务架构

微服务架构 一.传统的单体架构与微服务架构的区别1.1 单体架构1.1.1 优点1.1.2 缺点 1.2 微服务架构1.2.1 优点1.2.2 面临的问题与挑战 二. 微服务架构模式方案2.1 聚合器微服务2.2 链式微服务2.3 数据共享微服务2.4 异步消息传递微服务 三. SOA与微服务的区别 微服务&#xff…

JAVA毕业设计100—基于Java+Springboot+Vue的WMS仓库管理系统+移动端微信小程序(源码+数据库+部署视频)

基于JavaSpringbootVue的WMS仓库管理系统移动端(源码数据库部署视频) 一、系统介绍 本系统前后端分离带小程序 本系统分为管理员、用户角色(角色权限可自行分配) 功能列表&#xff1a; 1、 数据管理&#xff1a;物料数据管理、物料Bom管理、物料组管理、物料分类管理、供应…