Java 标准库 API 系列之 CyclicBarrier

CyclicBarrier也是Java中的一个同步工具类,它允许一组线程等待彼此达到一个共同的屏障点,直到所有线程都到达屏障点后才能继续执行。CyclicBarrier可以被认为是一个可重用的计数器,每当一个线程到达屏障点时,计数器减1,当计数器减为0时,表示所有线程都已到达屏障点,等待的线程可以继续执行。

API

CyclicBarrier的常用方法有两个:

  • int await():表示一个线程已经到达屏障点,等待其他线程,如果所有线程都到达屏障点,则计数器重置,并且所有线程都继续执行;
  • int await(long timeout, TimeUnit unit):表示一个线程已经到达屏障点,等待其他线程,如果所有线程都到达屏障点,则计数器重置,并且所有线程都继续执行;如果超时则抛出TimeoutException异常。

以下是一个使用CyclicBarrier进行线程同步的示例代码:

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

public class CyclicBarrierExample {
    private static CyclicBarrier barrier = new CyclicBarrier(3);

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println("Task 1 is running");
            try {
                barrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println("Task 1 is completed");
        });

        Thread t2 = new Thread(() -> {
            System.out.println("Task 2 is running");
            try {
                barrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println("Task 2 is completed");
        });

        Thread t3 = new Thread(() -> {
            System.out.println("Task 3 is running");
            try {
                barrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println("Task 3 is completed");
        });

        t1.start();
        t2.start();
        t3.start();

        Thread.sleep(1000);

        System.out.println("All tasks are started");

        t1.join();
        t2.join();
        t3.join();

        System.out.println("All tasks are completed");
    }
}

可重用表现在哪?

CyclicBarrier是可重用的,表现在以下两个方面:

  1. 计数器重置:当所有线程都到达屏障点后,计数器会被重置为初始值,即CyclicBarrier对象创建时传入的值,这样CyclicBarrier可以被用于多个屏障同步。在上述示例代码中,barrier变量在初始化时传入的是3,表示需要等待3个线程到达屏障点,如果需要再次使用该CyclicBarrier对象进行同步,只需调用barrier.reset()方法将计数器重置为3即可。
  2. 异常处理:如果一个线程在等待其他线程时被中断或超时,则CyclicBarrier会抛出BrokenBarrierExceptionTimeoutException异常,这样可以对异常进行处理后继续使用CyclicBarrier进行同步。在上述示例代码中,await()方法可能会抛出BrokenBarrierException异常,因此在调用await()方法时需要进行异常处理。

CyclicBarrier 和 CountDownLatch 的区别是什么?

之前我们介绍了CountDownLatch,似乎他们两个的作用是相同的,其实不然。

功能不同

CountDownLatch被作用于线程等待其他线程完成,然后被阻塞的线程才继续执行,而CyclicBarrier通常用于将多个线程的执行阶段进行同步,以便它们可以在栅栏位置进行等待,直到所有线程都达到栅栏位置,然后所有线程可以同时执行后续操作。

重用能力不同

CyclicBarrier可以被重用。一旦所有线程都到达栅栏位置并且栅栏被打破,它会自动重置,可以再次使用。

CountDownLatch不能被重用。一旦计数器达到零并且门闩被打开,它不能再次使用