JUC-CyclicBarrier

CyclicBarrier

基本使用

CyclicBarrier:循环屏障,用来进行线程协作,等待线程满足某个计数,才能触发自己执行

常用方法:

  • public CyclicBarrier(int parties, Runnable barrierAction):用于在线程到达屏障 parties 时,执行 barrierAction
    • parties:代表多少个线程到达屏障开始触发线程任务
    • barrierAction:线程任务
  • public int await():线程调用 await 方法通知 CyclicBarrier 本线程已经到达屏障

与 CountDownLatch 的区别:CyclicBarrier 是可以重用的

应用:可以实现多线程中,某个任务在等待其他线程执行完毕以后触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
CyclicBarrier barrier = new CyclicBarrier(2, () -> {
System.out.println("task1 task2 finish...");
});

for (int i = 0; i < 3; i++) { // 循环重用
service.submit(() -> {
System.out.println("task1 begin...");
try {
Thread.sleep(1000);
barrier.await(); // 2 - 1 = 1
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});

service.submit(() -> {
System.out.println("task2 begin...");
try {
Thread.sleep(2000);
barrier.await(); // 1 - 1 = 0
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
}
service.shutdown();
}

实现原理

成员属性

  • 全局锁:利用可重入锁实现的工具类

    1
    2
    3
    4
    // barrier 实现是依赖于Condition条件队列,condition 条件队列必须依赖lock才能使用
    private final ReentrantLock lock = new ReentrantLock();
    // 线程挂起实现使用的 condition 队列,当前代所有线程到位,这个条件队列内的线程才会被唤醒
    private final Condition trip = lock.newCondition();
  • 线程数量:

    1
    2
    private final int parties;	// 代表多少个线程到达屏障开始触发线程任务
    private int count; // 表示当前“代”还有多少个线程未到位,初始值为 parties
  • 当前代中最后一个线程到位后要执行的事件:

    1
    private final Runnable barrierCommand;
  • 代:

    1
    2
    3
    4
    5
    6
    7
    // 表示 barrier 对象当前 代
    private Generation generation = new Generation();
    private static class Generation {
    // 表示当前“代”是否被打破,如果被打破再来到这一代的线程 就会直接抛出 BrokenException 异常
    // 且在这一代挂起的线程都会被唤醒,然后抛出 BrokerException 异常。
    boolean broken = false;
    }
  • 构造方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public CyclicBarrie(int parties, Runnable barrierAction) {
    // 因为小于等于 0 的 barrier 没有任何意义
    if (parties <= 0) throw new IllegalArgumentException();

    this.parties = parties;
    this.count = parties;
    // 可以为 null
    this.barrierCommand = barrierAction;
    }

CountDownLatch与CyclicBarrier的使用场景与区别对比

CountDownLatch的使用场景:

在一些应用场合中,需要等待某个条件达到要求后才能做后面的事情;同时当线程都完成后也会触发事件,以便进行后面的操作,这个时候就可以使用CountDownLatch。 CountDownLatch最重要的方法是countDown()和await(),前者主要是倒数一次,后者是等待倒数到0,如果没有到达0,就只有阻塞等待了;

CyclicBarrier的使用场景:

CyclicBarrier可以用于多线程计算数据,最后合并计算结果的应用场景。比如我们用一个Excel保存了用户所有有很那个流水,每个Sheet保存一个账户近一年的每笔银行流水,现在需要统计用户的日均银行流水,先用多线程处理每个sheet里面的银行流水,都执行完成之后,得到每个sheet中的日均银行流水。最后,再用barrierAcition用这些线程的计算结果,计算出整个Excel的日均银行流水。

cyclicBarrier和CountDownLatch的区别是什么

  • a. CountDownLatch简单的说就是一个线程等待,直到它所等待的其他线程都执行完成并且调用countDown()方法发出通知后,当前线程才可以继续执行。
  • b. CyclicBarrier是所有线程都进行等待,直到所有线程都准备好进入await()方法之后,所有线程同时开始执行!
  • c.CountDownLatch的计数器只能使用一次,而cyclicBarrier的计数器可以使用reset()方法重置。所以CyclicBarrier能够处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程重新执行一次;
文章作者: GeYu
文章链接: https://nuistgy.github.io/2023/05/15/JUC-CyclicBarrier/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yu's Blog