详细介绍Java中的CyclicBarrier


Posted in Java/Android onApril 13, 2022

CyclicBarrier简介

对于CountDownLatch,其他线程为游戏玩家,比如英雄联盟,主线程为控制游戏开始的线程。在所有的玩家都准备好之前,主线程是处于等待状态的,也就是游戏不能开始。当所有的玩家准备好之后,下一步的动作实施者为主线程,即开始游戏。

对于CyclicBarrier,假设有一家公司要全体员工进行团建活动,活动内容为翻越三个障碍物,每一个人翻越障碍物所用的时间是不一样的。但是公司要求所有人在翻越当前障碍物之后再开始翻越下一个障碍物,也就是所有人翻越第一个障碍物之后,才开始翻越第二个,以此类推。类比地,每一个员工都是一个“其他线程”。当所有人都翻越的所有的障碍物之后,程序才结束。而主线程可能早就结束了,这里我们不用管主线程。

CyclicBarrier源码分析

类的继承关系

CyclicBarrier没有显示继承哪个父类或者实现哪个父接口, 所有AQS和重入锁不是通过继承实现的,而是通过组合实现的。

public class CyclicBarrier {}
```  

### 类的内部类

CyclicBarrier类存在一个内部类Generation,每一次使用的CycBarrier可以当成Generation的实例,其源代码如下

```java
private static class Generation {
boolean broken = false;
}

说明: Generation类有一个属性broken,用来表示当前屏障是否被损坏。

​类的属性

public class CyclicBarrier {

/** The lock for guarding barrier entry */
// 可重入锁
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
// 条件队列
private final Condition trip = lock.newCondition();
/** The number of parties */
// 参与的线程数量
private final int parties;
/* The command to run when tripped */
// 由最后一个进入 barrier 的线程执行的操作
private final Runnable barrierCommand;
/** The current generation */
// 当前代
private Generation generation = new Generation();
// 正在等待进入屏障的线程数量
private int count;
}

说明: 该属性有一个为ReentrantLock对象,有一个为Condition对象,而Condition对象又是基于AQS的,所以,归根到底,底层还是由AQS提供支持。

类的构造函数

CyclicBarrier(int, Runnable)型构造函数

public CyclicBarrier(int parties, Runnable barrierAction) {
// 参与的线程数量小于等于0,抛出异常
if (parties <= 0) throw new IllegalArgumentException();
// 设置parties
this.parties = parties;
// 设置count
this.count = parties;
// 设置barrierCommand
this.barrierCommand = barrierAction;
}

说明: 该构造函数可以指定关联该CyclicBarrier的线程数量,并且可以指定在所有线程都进入屏障后的执行动作,该执行动作由最后一个进行屏障的线程执行。

CyclicBarrier(int)型构造函数

public CyclicBarrier(int parties) {
// 调用含有两个参数的构造函数
this(parties, null);
}

说明: 该构造函数仅仅执行了关联该CyclicBarrier的线程数量,没有设置执行动作。

核心函数 - dowait函数

此函数为CyclicBarrier类的核心函数,CyclicBarrier类对外提供的await函数在底层都是调用该了doawait函数,

其源代码如下:

private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
// 保存当前锁
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) { // 正在等待进入屏障的线程数量为0,所有线程都已经进入
// 运行的动作标识
boolean ranAction = false;
try {
// 保存运行动作
final Runnable command = barrierCommand;
if (command != null) // 动作不为空
// 运行
command.run();
// 设置ranAction状态
ranAction = true;
// 进入下一代
nextGeneration();
return 0;
} finally {
if (!ranAction) // 没有运行的动作
// 损坏当前屏障
breakBarrier();
}
}

// loop until tripped, broken, interrupted, or timed out
// 无限循环
for (;;) {
try {
if (!timed) // 没有设置等待时间
// 等待
trip.await();
else if (nanos > 0L) // 设置了等待时间,并且等待时间大于0
// 等待指定时长
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) { // 等于当前代并且屏障没有被损坏
// 损坏当前屏障
breakBarrier();
// 抛出异常
throw ie;
} else { // 不等于当前带后者是屏障被损坏
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
// 中断当前线程
Thread.currentThread().interrupt();
}
}

if (g.broken) // 屏障被损坏,抛出异常
throw new BrokenBarrierException();

if (g != generation) // 不等于当前代
// 返回索引
return index;

if (timed && nanos <= 0L) { // 设置了等待时间,并且等待时间小于0
// 损坏屏障
breakBarrier();
// 抛出异常
throw new TimeoutException();
}
}
} finally {
// 释放锁
lock.unlock();
}
}

核心函数 - nextGeneration函数

此函数在所有线程进入屏障后会被调用,即生成下一个版本,所有线程又可以重新进入到屏障中,

其源代码如下:

private void nextGeneration() {
// signal completion of last generation
// 唤醒所有线程
trip.signalAll();
// set up next generation
// 恢复正在等待进入屏障的线程数量
count = parties;
// 新生一代
generation = new Generation();
}

在此函数中会调用AQS的signalAll方法,即唤醒所有等待线程。如果所有的线程都在等待此条件,则唤醒所有线程。

其源代码如:

public final void signalAll() {
if (!isHeldExclusively()) // 不被当前线程独占,抛出异常
throw new IllegalMonitorStateException();
// 保存condition队列头节点
Node first = firstWaiter;
if (first != null) // 头节点不为空
// 唤醒所有等待线程
doSignalAll(first);
}

到此这篇关于Java中的 CyclicBarrier详解的文章就介绍到这了!

Java/Android 相关文章推荐
解析Java异步之call future
Jun 14 Java/Android
Springboot如何使用logback实现多环境配置?
Jun 16 Java/Android
Java基于字符界面的简易收银台
Jun 26 Java/Android
spring cloud gateway中如何读取请求参数
Jul 15 Java/Android
在Spring-Boot中如何使用@Value注解注入集合类
Aug 02 Java/Android
idea以任意顺序debug多线程程序的具体用法
Aug 30 Java/Android
深入解读Java三大集合之map list set的用法
Nov 11 Java/Android
springmvc直接不经过controller访问WEB-INF中的页面问题
Feb 24 Java/Android
Java设计模式之享元模式示例详解
Mar 03 Java/Android
Android自定义scrollview实现回弹效果
Apr 01 Java/Android
Spring Boot 使用 Spring-Retry 进行重试框架
Apr 24 Java/Android
Java异常体系非正常停止和分类
Jun 14 Java/Android
Java8 Stream API 提供了一种高效且易于使用的处理数据的方式
Apr 13 #Java/Android
Java的Object类的九种方法
Apr 13 #Java/Android
Java 使用类型为Object的变量指向任意类型的对象
Apr 13 #Java/Android
java中为什么说子类的构造方法默认访问的是父类的无参构造方法
Apr 13 #Java/Android
Spring Cloud Netflix 套件中的负载均衡组件 Ribbon
Apr 13 #Java/Android
Android开发之WECHAT微信小程序路由跳转的两种形式
Apr 12 #Java/Android
JavaWeb Servlet开发注册页面实例
You might like
隐藏X-Space个人空间下方版权方法隐藏X-Space个人空间标题隐藏X-Space个人空间管理版权方法
2007/02/22 PHP
php获取网页内容方法总结
2008/12/04 PHP
php正则表达匹配中文问题分析小结
2012/03/25 PHP
php快递单号查询接口使用示例
2014/05/05 PHP
PHP+apc+ajax实现的ajax_upload上传进度条代码
2016/01/25 PHP
php进行ip地址掩码运算处理的方法
2016/07/11 PHP
php实现文章置顶功能的方法
2016/10/20 PHP
解决jQuery插件tipswindown与hintbox冲突
2010/11/05 Javascript
js如何获取file控件的完整路径具体实现代码
2013/05/15 Javascript
Javascript堆排序算法详解
2014/12/03 Javascript
基于jQuery ligerUI实现分页样式
2016/09/18 Javascript
微信小程序实现倒计时60s获取验证码
2020/04/17 Javascript
js数组实现权重概率分配
2017/09/12 Javascript
[04:00]DOTA2解说界神雕侠侣 CJ第四天谷子现场过生日
2013/07/30 DOTA
[01:04:29]DOTA2-DPC中国联赛 正赛 Phoenix vs XG BO3 第二场 1月31日
2021/03/11 DOTA
python使用Image处理图片常用技巧分析
2015/06/01 Python
学习python之编写简单简单连接数据库并执行查询操作
2016/02/27 Python
python中的字典使用分享
2016/07/31 Python
python 中字典嵌套列表的方法
2018/07/03 Python
Django migrations 默认目录修改的方法教程
2018/09/28 Python
python的sorted用法详解
2019/06/25 Python
python爬虫增加访问量的方法
2019/08/22 Python
用python写测试数据文件过程解析
2019/09/25 Python
python实现发送form-data数据的方法详解
2019/09/27 Python
python脚本实现音频m4a格式转成MP3格式的实例代码
2019/10/09 Python
使用Python生成200个激活码的实现方法
2019/11/22 Python
美国第二大团购网站:LivingSocial
2016/07/24 全球购物
LODI女鞋在线商店:阿利坎特的鞋类品牌
2019/02/15 全球购物
某公司面试题
2012/03/05 面试题
后勤人员自我评价怎么写
2013/09/19 职场文书
班级文化标语
2014/06/23 职场文书
创卫工作总结2015
2015/04/22 职场文书
签证工作证明模板
2015/06/15 职场文书
法制教育观后感
2015/06/17 职场文书
JavaScript 反射学习技巧
2021/10/16 Javascript
python数据处理之Pandas类型转换
2022/04/28 Python