CyclicBarrier是Java并发工具包中的一个重要组件,用于协调多个线程在某个点的同步操作。与CountDownLatch不同,CyclicBarrier可以被重用,这使得它在某些场景下更为灵活。本文将深入探讨CyclicBarrier的底层实现原理,包括其内部数据结构和工作机制。
一、CyclicBarrier的基本概念
CyclicBarrier的核心思想是让一组线程在某个屏障点等待,直到所有线程都到达屏障点后,才能继续执行后续的操作。它适用于需要将任务分解为多个子任务,并在所有子任务完成后才能继续执行的场景。
二、CyclicBarrier的内部数据结构
CyclicBarrier的核心数据结构包括一个ReentrantLock和一个Condition。ReentrantLock用于保护内部状态的修改,而Condition则用于线程的等待和唤醒。此外,CyclicBarrier还维护了一个count变量,用于记录当前还有多少线程未到达屏障点。
private final ReentrantLock lock = new ReentrantLock();
private final Condition trip = lock.newCondition();
private final int parties;
private int count;
private final Runnable barrierCommand;
- parties:表示需要等待的线程总数。
- count:表示当前尚未到达屏障点的线程数。
- barrierCommand:当所有线程到达屏障点后,会执行的一个可选任务。
三、CyclicBarrier的工作机制
- 初始化:在创建CyclicBarrier时,必须指定 - parties,即需要等待的线程数。- barrierCommand则是一个可选参数,用于在所有线程到达屏障点后执行的任务。
- 等待:当线程调用 - await()方法时,会先获取- lock,然后检查- count的值。如果- count减1后不为0,则当前线程会在- trip上等待。
- 屏障点释放:当最后一个线程调用 - await()方法时,- count减1后为0,此时会执行- barrierCommand(如果有的话),然后唤醒所有在- trip上等待的线程。
- 重置:与CountDownLatch不同,CyclicBarrier在屏障点释放后会自动重置 - count,从而可以再次用于下一次的等待。
四、CyclicBarrier的源码分析
下面是CyclicBarrier中await()方法的简化源码解析:
public int await() throws InterruptedException, BrokenBarrierException {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        final int index = --count;
        if (index == 0) {  // 最后一个线程到达屏障点
            Runnable command = barrierCommand;
            if (command != null) {
                try {
                    command.run();
                } catch (Throwable ex) {
                    breakBarrier();
                    throw ex;
                }
            }
            nextGeneration();  // 重置CyclicBarrier
            return 0;
        }
        // 不是最后一个线程,进入等待状态
        while (true) {
            try {
                trip.await();
                if (generation.broken)
                    throw new BrokenBarrierException();
                return index;
            } catch (InterruptedException ie) {
                breakBarrier();
                throw ie;
            }
        }
    } finally {
        lock.unlock();
    }
}
- nextGeneration():重置- count并唤醒所有等待的线程。
- breakBarrier():将屏障标记为“已损坏”,并唤醒所有等待的线程。
五、CyclicBarrier的使用场景
- 多线程任务分阶段执行:例如,一个任务需要分为多个阶段执行,每个阶段需要所有线程完成后才能进入下一阶段。
- 并行计算:将一个大型计算任务分解为多个子任务,所有子任务完成后才能输出最终结果。
六、CyclicBarrier的注意事项
- 线程中断:如果某个线程在等待时被中断,CyclicBarrier会进入“已损坏”状态,其他线程也会抛出BrokenBarrierException。
- 超时机制:CyclicBarrier提供了await(long timeout, TimeUnit unit)方法,可以设置超时时间,避免线程无限期等待。
七、总结
CyclicBarrier是Java并发编程中的一个强大工具,它通过内部锁和条件变量实现了线程的同步。理解其底层实现原理,可以帮助我们更好地使用它来优化并发程序的性能和可靠性。希望本文的深度解析能为你在并发编程中的实践提供有价值的参考。

