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级以上数据也能 ...
随机推荐
- 你知道第一个C语言C++编译器是如何诞生的吗?
当今几乎所有的实用的编译器/解释器(以下统称编译器)都是用 C 语言编写的,有一些语言比如 Clojure,Jython 等是基于 JVM 或者说是用 Java 实现的,IronPython 等是基于 ...
- 【坑爹的mybtis plus】wrapper.in击垮了我们的数据库!
mybatis plus让我们从很大程度上实现了用"java去写sql",但是有些很隐晦的使用方式,如果不注意的话,也会引起错误: 如果.in的时候给了一个null,这个时候并不会 ...
- mq内存映射
MappedFileQueue的封装 MappedFileQueue是MappedFile的管理容器,MappedFileQueue是对存储目录的封装. 查找MappedFile: 1.根据时间戳来查 ...
- Java数据结构(十二)—— 霍夫曼树及霍夫曼编码
霍夫曼树 基本介绍和创建 基本介绍 又称哈夫曼树,赫夫曼树 给定n个权值作为n个叶子节点,构造一棵二叉树,若该树的带权路径长度(wpl)达到最小,称为最优二叉树 霍夫曼树是带权路径长度最短的树,权值较 ...
- C# WinForm UDP 发送和接收消息
using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; ...
- 解决linux挖矿病毒(kdevtmpfsi,sysupdate, networkservice)
突然发现公司测试服务器CPU过高,是这两个sysupdate, networkservice进程,很明显是被挖矿了,记录下来以供参考. 病毒会把一些文件给加i锁或a锁,导致无法修改数据,所以某些操作需 ...
- 【GDKOI2014】JZOJ2020年8月13日提高组T4 内存分配
[GDKOI2014]JZOJ2020年8月13日提高组T4 内存分配 题目 Description Input Output 输出m行,每行一个整数,代表输入中每次程序变化后系统所需要的空闲内存单位 ...
- SQL Server 索引碎片整理
索引碎片整理的四种方法: 1)删除索引并重建 2)使用 DROP_EXISTING 语句重建索引 3)使用 ALTER INDEX REBUILD 语句重建索引 4)使用 ALTER INDEX RE ...
- Spring Cloud 学习 (六) Spring Cloud Config
在实际开发过程中,每个服务都有大量的配置文件,例如数据库的配置.日志输出级别的配置等,而往往这些配置在不同的环境中也是不一样的.随着服务数量的增加,配置文件的管理也是一件非常复杂的事 在微服务架构中, ...
- MyBatis 常见面试题总结
1.#{}和${}的区别是什么? 注:这道题是面试官面试我同事的. 答: ${}是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于静态文本替换,比如${drive ...