javalock(六)CyclicBarrier

注意:CyclicBarrier不是AQS的派生类,而是CyclicBarrier内部使用了ReentrantLock.Condition

和CountDownLatch一样,都是计数减为0就可以成功获取锁

和CountDownLatch不同的是:

  1:CountDownLatch的await和countdown操作是分开的即锁获取和锁释放是两个不同的操作,即一个线程先countdown,把计数-1,然后再await等待计数降为0,而CyclicBarrier中则合二为一了,就是说直接await,await一个函数做完两件事:countdown 扣减资源,然后await等待计数为02:CyclicBarrier是可充用的,也就是说唤醒一次后CyclicBarrier会重置状态,需要重新扣减计数,直到计数再次为0才会放行,而CountDownLatch则只其作用一次,一旦计数为0,则此后则不再恢复计数,也就是说此后任何请求都会直接放行

CyclicBarrier可重用的意思是内部有一个Generation变量,记录当前是第几代,如果计数为0就表示本轮计数完成了,然后通知所有阻塞的线程,通知完后再创建一个新的generation对象,就表示已经进入下一轮迭代了,其他线程被唤醒后如果检测到当前他休眠时记录的generation和当前的generation不是同一个,就表明generation已经进入下一轮了,所以自己可以成功唤醒了


import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class CyclicBarrier {private static class Generation {boolean broken = false;}//CyclicBarrier是基于ReentrantLock的条件变量的private final ReentrantLock lock = new ReentrantLock();//条件变量private final Condition trip = lock.newCondition();//记录初始限额,进入下一轮的时候会把计数恢复到初始限额private final int parties;//当计数降到0的时候执行指定的函数,由用户指定private final Runnable barrierCommand;//记录当前是第几轮,进入新的一轮计数时会创建一个新的generationprivate Generation generation = new Generation();//计数,每当count降为0就表明本轮结束了,可以唤醒所有等待的线程了//进入下一轮后会把count恢复到partiesprivate int count;//进入下一轮:通知上一轮所有等待的线程,通知完后会再更新状态以进入下一轮//这个函数在持有锁的情况下才会被调用private void nextGeneration() {//signalAll就是把trip这个condition对应的condition list中的所有node丢到sync list//丢完以后signalAll就结束了,但是其他线程只是丢到了sync list,并没有获取锁//所以其他被唤醒的线程需要重新竞争锁,但是nextGeneration是在有锁的情况下调用的//所以这里signalAll后就可以直接更新generation和count因为其他线程此时没有锁,//是做不了任何事的,所以这里更新generation不会造成并发错误trip.signalAll();count = parties;generation = new Generation();}//打破栅栏,也就是终止等待,强制唤醒所有等待的线程private void breakBarrier() {generation.broken = true;count = parties;//唤醒所有等待的线程,这些线程被唤醒后会检测到generation.broker=true//就明白了是被强制打断了本次计数,所以被唤醒的线程就会直接抛异常trip.signalAll();}//这是CyclicBarrier await的核心,做两件事:1:扣减计数;2:await计数归0private int dowait(boolean timed, long nanos)throws InterruptedException, BrokenBarrierException,TimeoutException {final ReentrantLock lock = this.lock;//先获取锁lock.lock();try {//记录本线程刚开始doawait时的generation变量//当线程再次唤醒时如果检测到CyclicBarrier对象的generation变量//和它记录的不是同一个generation变量,那么线程就知道他所在的轮次已经结束了//可能是因为计数归零从而被唤醒、超时、中断异常、被break强制中断等待final Generation g = generation;//在本线程还没开始doawait时就有其他线程执行了breakBarrier强制提前结束了本轮计数if (g.broken)//那么就直接抛异常throw new BrokenBarrierException();//如果本线程已经被中断if (Thread.interrupted()) {//就调用breakBarrier强制唤醒所有等待的线程,强制结束本轮计数breakBarrier();//然后再抛中断异常throw new InterruptedException();}//1:扣减计数int index = --count;//如果计数归零,就表明本轮计数已结束,可以唤醒所有线程if (index == 0) {  boolean ranAction = false;try {//当计数结束时,执行预定义的操作final Runnable command = barrierCommand;if (command != null)command.run();ranAction = true;//唤醒所有阻塞的线程,然后更新generation以进入下一轮迭代nextGeneration();return 0;} finally {//如果command不为null并且command.run抛异常了//就表明本轮计数失败,打上broken标记,然后强制唤醒所有线程//而这些被唤醒的线程会检测到broken,然后就抛异常了if (!ranAction)breakBarrier();}}//2:死循环等待被唤醒for (;;) {try {//如果没有开启超时if (!timed)//则直接condition.await,直到被其他线程signaltrip.await();else if (nanos > 0L)//否则awaitNanos,至多await这么多秒nanos = trip.awaitNanos(nanos);//如果await过程中抛出了中断异常} catch (InterruptedException ie) {//如果检测到被唤醒后时的generation和await之前的generation是同一个//则表明还是统一伦次//如果此事没有检测到被其他线程已经抛出broken//即其他人还在正常await,但是本线程已经检测到中断异常了//所以本线程就抛出broken并强制唤醒所有阻塞的线程//也就是说同一轮中只要有任意一个线程抛出了异常,// 就强制结束本轮,所有被唤醒的线程都会检测到broken然后抛异常if (g == generation && ! g.broken) {breakBarrier();throw ie;} else {//否则说明已经进入下一轮了或者已经有人抛出broken了//所以这里就简单标记一下中断Thread.currentThread().interrupt();}}//检查是否是因为broken被唤醒if (g.broken)//如果是则抛异常throw new BrokenBarrierException();//检查是否已经进入下一轮了,如果是的话则说明本轮正常结束了//说明本线程是被正常signal的,也就是说本线程成功结束了本次await//可以继续往下执行了,所以此时就返回本次线程的indexif (g != generation)return index;//如果还是同一轮,则说明还没有结束,因为一个线程只能扣减一次index//如果他在await前扣减完index后index不为0//则说明他不是最后一个到达的,还需要等待其他线程到达//所以这里检测是不是因为超时被唤醒的if (timed && nanos <= 0L) {//如果超时,则表示本轮计数失败//所以这里就强制broken并唤醒所有阻塞的线程//其他线程唤醒后就会检测到broken,然后就会抛出异常了breakBarrier();//抛出超时异常throw new TimeoutException();}}} finally {//释放锁//备注:当线程发现自己到达时index刚好为0即本线程是最后一个到达的,//所以本线程需要唤醒其他线程并更新generation和count//也就是执行nextGeneration()函数,此时还没有释放lock,所以此时是安全的//因为lock.unlock会等到执行完nextGeneration后才会在finally中执行lock.unlock();}}public CyclicBarrier(int parties, Runnable barrierAction) {if (parties <= 0) throw new IllegalArgumentException();this.parties = parties;this.count = parties;this.barrierCommand = barrierAction;}public CyclicBarrier(int parties) {this(parties, null);}public int getParties() {return parties;}public int await() throws InterruptedException, BrokenBarrierException {try {return dowait(false, 0L);} catch (TimeoutException toe) {throw new Error(toe); }}public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException {return dowait(true, unit.toNanos(timeout));}public boolean isBroken() {final ReentrantLock lock = this.lock;lock.lock();try {return generation.broken;} finally {lock.unlock();}}//重置barrierpublic void reset() {final ReentrantLock lock = this.lock;//先获取锁lock.lock();try {//然后强制broken并唤醒所有等待的线程//这些线程被唤醒后会检测到broken,然后就会抛异常breakBarrier();//唤醒其他线程后就更新generation表示进入下一轮nextGeneration(); } finally {//释放锁lock.unlock();}}//表示有多少个线程在等待public int getNumberWaiting() {final ReentrantLock lock = this.lock;lock.lock();try {//parties表示初始限额,count表示还剩几个线程没有到return parties - count;} finally {lock.unlock();}}
}

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

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

相关文章

Linux-ubuntu之主频和时钟配置

Linux-ubuntu之主频和时钟配置 一&#xff0c;主频二&#xff0c;其它时钟配置1.PLL2和PLL3的PFD0-3设置2.AHB_CLK_ROOT3.IPG 和 PERCLK时钟 三&#xff0c;总结 一&#xff0c;主频 24MHz 晶振为内核和其它外设提供时钟源&#xff0c;经电路后到PLL1变为996MHZ&#xff0c;再…

深入浅出Flink CEP丨如何通过Flink SQL作业动态更新Flink CEP作业

复杂事件处理&#xff08;CEP&#xff09;是一种对事件流进行分析的技术&#xff0c;它能够识别出数据流中的事件序列是否符合特定的模式&#xff0c;并允许用户对这些模式进行处理。Flink CEP 是 CEP 在 Apache Flink 中的具体实现&#xff0c;是 Apache Flink 的一个库&#…

华为数通最新题库 H12-821 HCIP稳定过人中

以下是成绩单和考试人员 HCIP H12-831 HCIP H12-725 安全中级

Facebook 与数字社交的未来走向

随着数字技术的飞速发展&#xff0c;社交平台的角色和形式也在不断演变。作为全球最大社交平台之一&#xff0c;Facebook&#xff08;现Meta&#xff09;在推动数字社交的进程中扮演了至关重要的角色。然而&#xff0c;随着互联网的去中心化趋势和新技术的崛起&#xff0c;Face…

STM32中ADC模数转换器

一、ADC简介 ADC模拟-数字转换器 ADC可以将引脚连续变化的模拟电压转换为内存中存储的数字变量&#xff0c;建立模拟电路到数字电路的桥梁 12位逐次逼近型ADC&#xff0c;1us转换时间 输入电压范围&#xff1a; 0~3.3V&#xff0c;转换结果范围&#xff1a;0~4095 18个输入…

fpga系列 HDL:Quartus II PLL (Phase-Locked Loop) IP核 (Quartus II 18.0)

在 Quartus II 中使用 PLL (Phase-Locked Loop) 模块来将输入时钟分频或倍频&#xff0c;并生成多个相位偏移或频率不同的时钟信号&#xff1a; 1. 生成 PLL 模块 在 Quartus II 中&#xff1a; 打开 IP Components。 file:///C:/intelFPGA_lite/18.0/quartus/common/help/w…

Springboot3.x配置类(Configuration)和单元测试

配置类在Spring Boot框架中扮演着关键角色&#xff0c;它使开发者能够利用Java代码定义Bean、设定属性及调整其他Spring相关设置&#xff0c;取代了早期版本中依赖的XML配置文件。 集中化管理&#xff1a;借助Configuration注解&#xff0c;Spring Boot让用户能在一个或几个配…

【游戏中orika完成一个Entity的复制及其Entity异步落地的实现】 1.ctrl+shift+a是飞书下的截图 2.落地实现

一、orika工具使用 1)工具类 package com.xinyue.game.utils;import ma.glasnost.orika.MapperFactory; import ma.glasnost.orika.impl.DefaultMapperFactory;/*** author 王广帅* since 2022/2/8 22:37*/ public class XyBeanCopyUtil {private static MapperFactory mappe…

Unity 组件学习记录:Aspect Ratio Fitter

概述 Aspect Ratio Fitter是 Unity 中的一个组件&#xff0c;用于控制 UI 元素&#xff08;如Image、RawImage等&#xff09;的宽高比。它在处理不同屏幕分辨率和尺寸时非常有用&#xff0c;可以确保 UI 元素按照预期的比例进行显示。当添加到一个 UI 对象上时&#xff0c;Aspe…

uni-app开发AI康复锻炼小程序,帮助肢体受伤患者康复!

**提要&#xff1a;**近段时间我们收到多个康复机构用户&#xff0c;咨询AI运动识别插件是否可以应用于肢力运动受限患者的康复锻炼中来&#xff0c;插件是可以应用到AI康复锻炼中的&#xff0c;今天小编就为您介绍一下AI运动识别插件在康腹锻炼中的应用场景。 一、康复机构的应…

Elasticsearch:什么是信息检索?

信息检索定义 信息检索 (IR) 是一种有助于从大量非结构化或半结构化数据中有效、高效地检索相关信息的过程。信息&#xff08;IR&#xff09;检索系统有助于搜索、定位和呈现与用户的搜索查询或信息需求相匹配的信息。 作为信息访问的主要形式&#xff0c;信息检索是每天使用…

Pytest-Bdd vs Behave:选择最适合的 Python BDD 框架

Pytest-Bdd vs Behave&#xff1a;选择最适合的 Python BDD 框架 Pytest BDD vs Behave&#xff1a;选择最适合的 Python BDD 框架BDD 介绍Python BDD 框架列表Python BehavePytest BDDPytest BDD vs Behave&#xff1a;关键区别Pytest BDD vs Behave&#xff1a;最佳应用场景结…

【数据集】5种常见人类行为检测数据集3379张YOLO+VOC格式

数据集格式&#xff1a;VOC格式YOLO格式 压缩包内含&#xff1a;3个文件夹&#xff0c;分别存储图片、xml、txt文件 JPEGImages文件夹中jpg图片总计&#xff1a;3379 Annotations文件夹中xml文件总计&#xff1a;3379 labels文件夹中txt文件总计&#xff1a;3379 标签种类数&am…

唯品会Android面试题及参考答案

HTTP 和 HTTPS 的区别是什么?你的项目使用的是 HTTP 还是 HTTPS? HTTP 和 HTTPS 主要有以下区别。 首先是安全性。HTTP 是超文本传输协议,数据传输是明文的,这意味着在数据传输过程中,信息很容易被窃取或者篡改。比如,在一个不安全的网络环境下,黑客可以通过网络嗅探工具…

基于Python+Vue开发的商城管理系统,大四期末作业,实习作品

项目简介 该项目是基于PythonVue开发的商城管理系统&#xff08;前后端分离&#xff09;&#xff0c;这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Python编程技能&#xff0c;同时锻炼他们的项目设计与开发能力。通过学习基于Python的网上商城管…

在 Solana 上实现 SOL 转账及构建支付分配器

与以太坊不同&#xff0c;在以太坊中&#xff0c;钱包通过 msg.value 指定交易的一部分并“推送” ETH 到合约&#xff0c;而 Solana 程序则是从钱包“拉取” Solana。 因此&#xff0c;没有“可支付”函数或“msg.value”这样的概念。 下面我们创建了一个新的 anchor 项目&a…

WebRTC搭建与应用(一)-ICE服务搭建

WebRTC搭建与应用(一) 近期由于项目需要在研究前端WebGL渲染转为云渲染&#xff0c;借此机会对WebRTC、ICE信令协议等有了初步了解&#xff0c;在此记录一下&#xff0c;以防遗忘。 第一章 ICE服务搭建 文章目录 WebRTC搭建与应用(一)前言一、ICE是什么&#xff1f;二、什么…

Linux高性能服务器编程 | 读书笔记 | 12. 多线程编程

12. 多线程编程 注&#xff1a;博客中有书中没有的内容&#xff0c;均是来自 黑马06-线程概念_哔哩哔哩_bilibili 早期Linux不支持线程&#xff0c;直到1996年&#xff0c;Xavier Leroy等人开发出第一个基本符合POSIX标准的线程库LinuxThreads&#xff0c;但LinuxThreads效率…

查看Mysql数据库引擎以及修改引擎为innoDB

目录 打开Mysql命令行 打开Mysql命令行 SHOW ENGINES;innoDB在事务型数据库中应用最多&#xff0c;其主要支持事务安全表&#xff08;ACID&#xff09;&#xff0c;行锁定和外键。 介绍下InnoDB的主要特性&#xff1a; 1、InnoDB给MySQL提供了具有提交、回滚和崩溃恢复能力的事…

Moretl安全日志采集工具

永久免费: 至Gitee下载 使用教程: Moretl使用说明 使用咨询: 用途 定时全量或增量采集工控机,电脑文件或日志. 优势 开箱即用: 解压直接运行.不需额外下载.管理设备: 后台统一管理客户端.无人值守: 客户端自启动,自更新.稳定安全: 架构简单,兼容性好,通过授权控制访问. 架…