CyclicBarrier回环屏障深度解析
1. 前沿
从上一节的CountDownLatch的学习,我们发现其只能使用一次,当state递减为0后,就没有用了,需要重新新建一个计数器。那么我们有没有可以复用的计数器呢?当然,JUC包给我们提供了CyclicBarrier回环屏障来实现计数器的复用。
2. 概念讲解
何为回环:当所有等待线程执行完成后,重置CyclicBarrier的状态,使得它能被复用。
何为屏障:线程调用await方法后就会被阻塞,这个阻塞点就叫屏障点,等所有线程都调用await方法后,线程们就会突破屏障,继续往下运行。
3. 案例

4. 源码分析
- 构造函数
// 一个入参的构造函数
public CyclicBarrier(int parties) {
// 调用还是两个入参的构造函数
this(parties, null);
} public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
// 将入参计数器值赋给count和线程数parties
this.parties = parties;
this.count = parties;
// 计数器count=0时,执行下面barrierCommand的功能
this.barrierCommand = barrierAction;
}
- await()
public int await() throws InterruptedException, BrokenBarrierException {
try {
// 实际调用的dowait方法
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
// 回环屏障底层是用ReentrantLock独占锁实现,其底层还是基于AQS实现
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
// 如果回环屏障是broken=true状态,抛出异常。因为dowait之前broken一定是false的
if (g.broken)
throw new BrokenBarrierException();
// 如果线程interrupted了,那么打破屏障,代码往下执行
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
// 计数器递减1
int index = --count;
// 当计数器为0时,打破屏障
if (index == 0) { // tripped
boolean ranAction = false;
try {
// 我们代码中调用两个参数的构造函数时,传入的一个任务
final Runnable command = barrierCommand;
// 如果任务不为空,那么在回环屏障打破的时候,执行我们自定义的任务。
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
// 如果try方法出现异常,没有正常将ranAction设置为true,那么finally里强行打破屏障
if (!ranAction)
breakBarrier();
}
}
// 以下是设置了超时时间的dowait方法实现,当等待时间超过了超时时间时,屏障等待也会结束
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
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) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
// 打破屏障的方法
private void breakBarrier() {
// 设置打破状态为true
generation.broken = true;
// 将线程数重新赋给计数器count,这是保证屏障复用的关键。
count = parties;
// 唤醒其他所有因为调用await方法而发生阻塞的线程
trip.signalAll();
}
5. 总结
当我们创建回环屏障对象时,传入的计数器值M,前M-1个线程调用await方法时,获得独占锁,串行话执行dowait方法,都将count递减1,并且将M-1个线程加入到trip的条件队列中去。当最后一个线程执行到await方法时,最终将count置为了0,同时唤醒trip条件队列中所有被阻塞的线程,使得所有的M个线程继续往下执行。
CyclicBarrier回环屏障深度解析的更多相关文章
- 回环屏障CyclicBarrier
上一篇说的CountDownLatch是一个计数器,类似线程的join方法,但是有一个缺陷,就是当计数器的值到达0之后,再调用CountDownLatch的await和countDown方法就会立刻返 ...
- thread_CyclicBarrier回环栅栏
CyclicBarrier回环栅栏,字面意思是可循环使用(Cyclic)的屏障(Barrier).通过它可以实现让一组线程等待至某个状态之后再全部同时执行. 它要做的事情是,让一组线程到达一个屏障(也 ...
- 回环栅栏CyclicBarrier
通过它可以实现让一组线程等待至某个状态之后再全部同时执行.叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用.我们暂且把这个状态就叫做barrier,当调用await()方 ...
- 并发编程-concurrent指南-回环栅栏CyclicBarrier
字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行. java.util.concurrent.CyclicBarrier 类是一种同步机制,它能够对处理一些算法的线程实现同步 ...
- 一个基于深度学习回环检测模块的简单双目 SLAM 系统
转载请注明出处,谢谢 原创作者:Mingrui 原创链接:https://www.cnblogs.com/MingruiYu/p/12634631.html 写在前面 最近在搞本科毕设,关于基于深度学 ...
- ORB-SLAM(六)回环检测
上一篇提到,无论在单目.双目还是RGBD中,追踪得到的位姿都是有误差的.随着路径的不断延伸,前面帧的误差会一直传递到后面去,导致最后一帧的位姿在世界坐标系里的误差有可能非常大.除了利用优化方法在局部和 ...
- Kafka深度解析
本文转发自Jason’s Blog,原文链接 http://www.jasongj.com/2015/01/02/Kafka深度解析 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅 ...
- mybatis 3.x源码深度解析与最佳实践(最完整原创)
mybatis 3.x源码深度解析与最佳实践 1 环境准备 1.1 mybatis介绍以及框架源码的学习目标 1.2 本系列源码解析的方式 1.3 环境搭建 1.4 从Hello World开始 2 ...
- Kafka深度解析(如何在producer中指定partition)(转)
原文链接:Kafka深度解析 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅的消息系统.主要设计目标如下: 以时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能 ...
随机推荐
- 牛客练习赛66 C公因子 题解(区间gcd)
题目链接 题目大意 给你一个长为n的数组,给所有数组元素加上一个非负整数x,使得这个数组的所有元素的gcd最大 题目思路 这主要是设计到一个多个数gcd的性质 gcd(a,b,c,d.....)=gc ...
- ESP8266 ESP-01制作B站粉丝计数器 | Arduino IDE开发ESP8266 | ESP8266存储读取用户数据
1.实物图 2.原理图 3.Arduino程序 在程序中修改自己的B站UID和自己家的WiFi名称及密码即可. /*B站粉丝计数器*/ /******************************* ...
- 数据库default null字段用基本类型映射,改成包装类型后缓存中旧数据反序列化失败
rt,spring Temp不知道用的什么反序列化,int不能反序列化为Integer,后实验hissing是可以的int->Integer Integer(不为null)->int均可
- pixi.js持续渲染页面
Pixi是一个超快的2D渲染引擎,通过Javascript和Html技术创建动画或管理交互式图像,从而制作游戏或应用. 项目地址:https://github.com/pixijs/pixi.js A ...
- charles功能(二)修改response请求参数
1.接口处 鼠标右击,选择breakpoints(允许本接口使用breakpionts功能) 2.开始设置断点值 4.重新请求接口(charles的界面变为可编辑状态),修改请求参数,执行请求 5.最 ...
- 强大的拉姆表达式转Sql 类库 - SqlSugar 隐藏功能之Lambda
使用场景 1.Lambda to sql 一直是ORM中最难的功能之一,如果有现成的解析库那么自已写一个ORM难度将大大降低 2.通过Lambda作为KEY进行缓存操作,特别是仓储模式想要拿到表达式进 ...
- PyQt(Python+Qt)学习随笔:QListWidget的访问当前项的currentItem和setCurrentItem方法
老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 currentItem方法返回列表部件当前选择的项,setCurrentItem方法用于设置当前项. ...
- 使用k8s部署springboot+redis简单应用
准备 本文将使用k8s部署一个springboot+redis应用,由于是示例,所以功能比较简单,只有设置值和获取值两个api. (1)设置值 (2)获取值 构建Web应用 (1)创建一个spring ...
- Asp.net core中RedisMQ的简单应用
最近一个外部的项目,使用到了消息队列,本来是用rabbitmq实现的,但是由于是部署到别人家的服务器上,想尽量简化一些,项目中本来也要接入了redis缓存,就尝试使用redis来实现简单的消息队列. ...
- 一文看懂 Kubernetes 服务发现: Service
Service 简介 K8s 中提供微服务的实体是 Pod,Pod 在创建时 docker engine 会为 pod 分配 ip,"外部"流量通过访问该 ip 获取微服务.但 ...