网站首页 > 博客文章 正文
上一篇聊了一下Semaphore信号灯的用法及源码,这一篇来聊一下CyclicBarrier的用法及解析。
官网解释:
- 允许一组线程全部等待彼此达到共同屏障点的同步辅助。循环阻塞在涉及固定大小的线程方的程序中很有用,这些线程必须偶尔等待彼此。屏障被称为循环 ,因为它可以在等待的线程被释放之后重新使用。
意思就是每个线程都得执行到等待点进行等待,直到所有线程都执行到等待点,才会继续往下执行。相当于日常开会,只有等每个参会的人都到之后才会开始会议。
用法:(以开会举例)
1、会议需要三个人
CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {
@Override
public void run()
2、这是三个人都到齐之后会执行的代码
System.out.println("三个人都已到达会议室")
}
});
3、定义三个线程,相当于三个参会的人
for (int i = 0; i < 3; i++) {
final int finalI = i;
new Thread(new Runnable() {
@Override
public void run() {
try {
4、模拟每人到会议室所需时间
Thread.sleep((long) (Math.random()*5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第"+Thread.currentThread().getName()+"个人到达会议室");
try {
5、等待其他人到会议室
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"开始开会");
}
}, String.valueOf(finalI)).start();
}
上述代码运行的结果为:
源码解析:
一、构造方法
有两个构造方法,只有带Runnable参数的构造方法才会在所有线程都到达等待点之后执行Runnable里面的run方法。
CyclicBarrier(int parties) {
this(parties, null);
}
CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
二、维护锁状态逻辑
其底层使用ReentrantLock+Condition进行锁状态的维护
1、维护锁状态
private final ReentrantLock lock = new ReentrantLock();
private final Condition trip = lock.newCondition();
2、线程组数
private final int parties;
3、所有线程到达等待点后执行的Runnable
private final Runnable barrierCommand;
4、需要等待的线程数量
private int count;
6、屏障点定义
private static class Generation {
boolean broken = false;
}
具体看看其是如何实现等待逻辑的,线程等待需要调用await方法
public int await() {
return dowait(false, 0L);
}
public int await(long timeout, TimeUnit unit){
return dowait(true, unit.toNanos(timeout));
}
最终调用的是dowait方法
private int dowait(boolean timed, long nanos){
final ReentrantLock lock = this.lock;
1、获取锁
lock.lock();
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
2、如果线程中断,重置等待线程数量并且唤醒当前等待的线程
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
3、等待线程数减1
int index = --count;
4、当等待线程数为 时
if (index == 0) { // tripped
boolean ranAction = false;
try {
5、执行所有线程都到达等待点之后的Runnable
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
6、唤醒所有线程并生成下一代
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
7、如果等待线程数不为0
for (;;) {
try {
8、根据传入的参数来决定是定时等待还是非定时等待
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
9、线程中断之后唤醒所有线程并进入下一代
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
Thread.currentThread().interrupt();
}
}
10、如果线程因为打翻屏障操作而被唤醒则抛出异常
if (g.broken)
throw new BrokenBarrierException();
11、如果线程因为换代操作而被唤醒则返回计数器的值
if (g != generation)
return index;
12、如果线程因为时间到了而被唤醒则打翻栅栏并抛出异常
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
可以看到,是通过index字段控制线程等待的,当index不为0的时候,线程统一会进行阻塞,直到index为0的时候,才会唤醒所有线程,这时候所有线程才会继续往下执行。
三、重复使用
这个跟CountdownLatch不一样的是,CountdownLatch是一次性的,而CycliBarrier是可以重复使用的,只需调用一下reset方法。
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
1、破坏当前的屏障点并唤醒所有线程
breakBarrier();
2、生成下一代
nextGeneration();
} finally {
lock.unlock();
}
}
private void breakBarrier() {
generation.broken = true;
将等待线程数量重置
count = parties;
唤醒所有线程
trip.signalAll();
}
private void nextGeneration() {
唤醒所有线程
trip.signalAll();
将等待线程数量重置
count = parties;
generation = new Generation();
}
上述就是对CycliBarrier的解析。
=======================================================
我是Liusy,一个喜欢健身的程序员。
欢迎关注微信公众号【Liusy01】,一起交流Java技术及健身,获取更多干货,领取Java进阶干货,一起成为Java大神。
来都来了,关注一波再溜呗。
- 上一篇: CyclicBarrier简单使用
- 下一篇: 岂止于大,一文读懂大数据及其在推荐系统的应用
猜你喜欢
- 2025-01-01 第一篇|Spark概览
- 2025-01-01 蔚来真题和答案,主打一个简单?
- 2025-01-01 ABB工业机器人编程指令函数程序数据等
- 2025-01-01 eNodeB启动和RRC连接建立
- 2025-01-01 硬盘SMART检测参数详解
- 2025-01-01 富集分析,看完这篇就够
- 2025-01-01 18个并发场景的设计模式详解,有没有你的盲区
- 2025-01-01 并发编程常见问题
- 2025-01-01 阿里架构师亲自梳理的:多线程与高并发知识点
- 2025-01-01 应读者要求讲讲 DMA
你 发表评论:
欢迎- 最近发表
-
- 告别频繁登录!Nuxt3 + TS + Vue3实战:双Token无感刷新方案全解析
- SpringBoot实现单点登录(SSO)的4种方案
- 随机密聊 匿名聊天室程序源码(随机匿名聊天在线)
- SpringBoot大文件上传卡死?分块切割术搞定GB级传输,速度飙升!
- Java 微服务从源码实战开始 | Gitee 项目推荐
- 轻量级埋点sdk搭建,便捷更全面(埋点sdk是什么)
- Spring Boot 实现文件秒传功能(springboot上传文件到指定文件夹)
- 项目中不用redis分布式锁,怎么防止用户重复提交?
- SpringBoot项目日志打印traceId生成
- 如何实现PC端网站扫码登录操作?(网页 扫码)
- 标签列表
-
- ifneq (61)
- 字符串长度在线 (61)
- googlecloud (64)
- flutterrun (59)
- 系统设计图 (58)
- powershellfor (73)
- messagesource (71)
- plsql64位 (73)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- qcombobox样式表 (68)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)