首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

CyclicBarrier 栅栏入门使用及源码详解

栅栏(Barrier)

简介

栅栏(Barrier)类似于闭锁,它能阻塞一组线程直到某个事件发生。

闭锁是一次性对象,一旦进入最终状态,就不能被重置了。

栅栏与闭锁的关键区别在于,所有线程必须同时达到栅栏位置,才能继续执行。

闭锁用于等待事件,而栅栏用于等待其他线程

而我再等你点赞。

使用场景

栅栏用于实现一些协议,例如几个小伙伴定在某个地方集合:“明天 6:00 在老马家碰头,到了以后要等其他人,之后再讨论去哪里玩。”

CyclicBarrer可以使一定数量的参与方反复地在栅栏位置汇集,它在并行迭代算法中非常有用:这种算法通常将一个问题拆分一些列互相独立的子问题。

如果所有线程都达到了栅栏位置,那么栅栏将打开,为此所有线程都被释放,而栅栏将被重置以便下次使用。如果对await的调用超时,或者await阻塞的线程被中断,那么栅栏就认为是打破了,所有阻塞的await调用都将终止并抛出BrokenBarrerException。

如果成功通过栅栏,那么await将为每个线程返回一个唯一的到达索引号,我们可以利用这些索引来“选举”产生一个领导线程,并在下一次迭代中由该领导线程执行一些特殊的工作。

CyclicBarrer还可以使你将一个栅栏操作传递给构造函数,这是Runnable,当成功通过栅栏时会(在一个子任务线程中)执行它,但在阻塞线程被释放之前是不能被执行的。

在模拟程序中通常需要使用栅栏,例如某个步骤中的计算可以并行执行,但必须等到该步骤中的所有计算都执行完成才能进入下一个步骤。

例子

代码实现

我们首先定义一个 Writer 模拟我们需要处理的子任务:

线程通过沉睡 5S 来模拟平时的工作执行耗时,然后使用 等待其他线程执行完成。

最后统一输出全部:所有线程写入完毕,继续处理其他任务…

这个就等价于约定好早晨 6:00 到老马家,各位小伙伴都到了,就可以继续进行后去的任务安排了。

任务的执行,我们这里定义了 4 个需要执行的任务。

并且还演示了 CyclicBarrier 非常方便的一个特性,使用 就可以重用,非常符合环保节能可持续发展的精神。

测试日志

对应的日志如下:

CyclicBarrier 源码

看完了 CyclicBarrier 的使用例子,让我们一起学习一下 CyclicBarrier 的源码吧。

jdk 版本

老马本次阅读 jdk 的版本是:

Generation

每一个栅栏都有一个对应的 Generation 维护对应的 broken 状态。

基本属性

可以看到,这里用到了 ReentrantLock 可重入锁及对应的 Condition 等。

构造器

我们最常用的就是第一个构造器,可以指定需要等个几个线程。

这里主要初始化 parties 和 count 的值。

barrierCommand 应该是一个可执行的 Runnable 任务,可以在 tripped 状态时执行。

await 方法

我们一起来看一下最常用的 await 方法:

当然,也有对应的指定超时时间的版本;

这里实际上调用的都是 dowait 方法:

好家伙,洋洋洒洒几十行,为了便于阅读,老马将解析写在代码注释中。

中断栅栏

这里是直接设置 broken 的值为 true,然后唤醒所有等待线程。

nextGeneration 更新 generation

唤醒所有的等待者,并且重新设置 generation。

小结

CyclicBarrier 作为一个并发的控制工具,和 CountDownLatch 对比个人感觉最大的优势就是可重用。而且多个线程执行完成后,才能继续执行,非常适合多线程任务拆分执行等场景。

希望本文对你有帮助,如果有其他想法的话,也可以评论区和大家分享哦。

各位极客的点赞收藏转发,是老马持续写作的最大动力!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20201113A0GAV900?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券