Java中的CyclicBarrier概述

CyclicBarrier 是 Java 并发包 java.util.concurrent 里的同步工具类。它允许一组线程在到达某个公共屏障点(common barrier point)时相互等待,直到所有线程都到达该屏障点,然后所有线程才会继续执行。

CyclicBarrier 有一个计数器,在创建时需指定参与等待的线程数量。当一个线程调用 await() 方法,它就会被阻塞,直到所有线程都调用了 await() 方法,此时计数器归 0,所有被阻塞的线程会同时被释放继续执行。而且,CyclicBarrier 可以被重复使用,即一轮同步完成后,它可以重新开始下一轮同步。

CyclicBarrier与CountDownLatch的区别

1. 可重用性

  • CyclicBarrier:具有可重用性,在一轮同步完成后,计数器会重置,能开启下一轮同步。
  • CountDownLatch:不可重用,计数器一旦减到 0,就无法再恢复初始值,不能进行新一轮的等待。

2. 侧重点

  • CyclicBarrier:强调的是多个线程之间相互等待,所有线程到达屏障点后才一起继续执行,更注重线程之间的协作。
  • CountDownLatch:侧重于一个或多个线程等待其他线程完成操作,是单向的等待。

3. 计数器操作方式

  • CyclicBarrier:每个线程调用 await() 方法时,计数器会自动减 1,直到为 0 时所有线程继续执行。
  • CountDownLatch:需要在其他线程中手动调用 countDown() 方法来减少计数器的值。

4. 构造函数和功能

  • CyclicBarrier:构造函数可以传入一个 Runnable 任务,当所有线程到达屏障点后,会先执行这个任务,然后再让所有线程继续执行。
  • CountDownLatch:没有类似的功能。

使用场景

CyclicBarrier的使用场景

  • 并行计算:在进行大规模数据处理时,可将数据分成多个部分,让多个线程并行处理,当所有线程完成各自部分的处理后,在 CyclicBarrier 处等待,全部完成后一起进行结果合并。
  • 多玩家游戏:在多人在线游戏中,当所有玩家都准备好后,游戏才能开始。可以使用 CyclicBarrier 让所有玩家线程在准备好后在屏障处等待,等所有玩家都准备好后同时开始游戏。

CountDownLatch的使用场景

  • 主线程等待子线程完成任务:比如主线程需要等待多个子线程完成数据加载、初始化等操作后,再进行后续处理。
  • 并行任务启动:多个并行任务同时启动,主线程等待所有任务都启动完成后再进行下一步操作。

以下是一个 CyclicBarrier 的简单示例代码:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        int threadCount = 3;
        // 创建 CyclicBarrier 对象,指定线程数量和所有线程到达屏障后执行的任务
        CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
            System.out.println("All threads have reached the barrier.");
        });

        for (int i = 0; i < threadCount; i++) {
            final int threadId = i;
            new Thread(() -> {
                try {
                    System.out.println("Thread " + threadId + " is working...");
                    // 模拟线程工作
                    Thread.sleep((long) (Math.random() * 1000));
                    System.out.println("Thread " + threadId + " has reached the barrier.");
                    // 线程到达屏障点,等待其他线程
                    barrier.await();
                    System.out.println("Thread " + threadId + " continues after the barrier.");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

在这个示例中,创建了一个 CyclicBarrier 对象,指定有 3 个线程参与同步,并传入一个 Runnable 任务。每个线程模拟执行任务,然后调用 await() 方法在屏障处等待,当所有线程都到达屏障点后,会先执行传入的 Runnable 任务,然后所有线程继续执行后续代码。