Skip to content

CyclicBarrier

CyclicBarrier (Java SE 21 & JDK 21)

CyclicBarrier 实现的作用是等人齐的操作,他设置了一个屏障,规定必须有多少个线程到达之后才能一起去执行下面的任务

大致逻辑是这样的:当线程跑到这个屏障的时候,他会去报个到,然后计数器减一,如果计数器不是0,那么就去睡眠,如果计数器归零,那么就去唤醒所有在等待的线程,大家一起接着执行

内部维护变量:

  • ReentrantLock lock:用来保证一致性的
  • Condition trip:用来使线程阻塞和批量唤醒线程
  • int parties:表示需要等待的线程数,可以在构造中传入
  • Runnable barrierCommand:当达到启动的条件的时候执行的函数,可以在构造中传入
  • Generation generation:内部维护一个布尔变量,表示当前屏障是否损坏
  • int count:还差多少线程才能启动

阅读文档发现这玩意很简单,就几个东西,看看重要的:

  • 初始化:可以只传入一个 int 值,表示这个屏障需要等待多少线程,还可以额外传入一个 Runnable 表示到齐后执行的操作
  • await:其实就是将计数器 -1,直到最后有一个线程也触发了,然后大家都启动,还可以传入一个超时时间,如果超时就破坏屏障,报错
  • reset:重置操作,这意味着这个东西是可以重复使用的

观看源码发现,两个 await 方法都指向了 dowait 方法,这个才是核心:

  • 其使用 lock 来保证只有一个线程操作计数器
  • 首先判断屏障是否损坏,如果损坏就报错
  • 然后判断是否被中断,如果被中断,会将屏障置为破坏状态,也报错
  • 然后去递减计数器,如果计数器为0,并且传入了 barrierCommand,就去执行,然后更新下一代屏障,报错了就破坏屏障,没问题就更新屏障
  • 计数器不为零的情况下就阻塞自己,如果传入了等待时间就阻塞指定的时间,这时候有个操作,如果线程在别的地方被中断了,他会捕获并且检查屏障状态,如果屏障完好,就说明确实出问题了,他就去破坏屏障并报错,反之说明任务完成,就不需要破坏屏障,就给自己补一个中断标签
  • 被唤醒之后,他会去检查为什么被唤醒的,如果 generation 说屏障坏了,就报错,如果 generation 进行入了下一代,就返回当前的 Index 表示自己是几号来的,如果是超时了,就破坏屏障,报错

其实这里面设计了两个操作:更新屏障和破坏屏障,主要的作用就是去让这一组等待的线程保持一致性,要么成功,要么失败

  • nextGeneration (更新):他会去唤醒所有线程,然后重置 count,然后去用一个新的 Generation 替换原来的,表示成功
  • breakBarrier(破坏):他会去将 generation 中的布尔类型置为 true,表示损坏了,然后重置计数器,然后唤醒所有线程,表示失败了

  • Q:为啥 nextGeneration 会先唤醒线程然后再更新 Generation 呢,不怕其他线程抢先检测出是否更新吗

    A:并不会,这些所有的操作都是在 lock 中进行的,其他线程必须等待这三个操作结束之后才执行

Contact me: 1943284256@qq.com