在 Java 多线程开发中,我们经常需要协调多个线程的执行。你有没有遇到过这样的场景:一个复杂任务被拆分成多个子任务并行执行,但所有子任务必须在某个点等待彼此完成后才能一起进入下一阶段。这时,CyclicBarrier 就能大显身手了!
CyclicBarrier 是什么?
CyclicBarrier(循环屏障)是 JDK 1.5 引入的 java.util.concurrent 包中的同步工具类,它的名字很形象 - "循环"意味着可以重复使用,"屏障"表示它能阻塞一组线程直到所有线程都到达某个点。
简单来说,CyclicBarrier 允许一组线程互相等待,直到所有线程都到达一个公共屏障点(barrier point),然后所有线程才会继续执行。更牛的是,它还支持在所有线程到达屏障时执行一个预定义的操作!

CyclicBarrier 与其他同步工具对比
CyclicBarrier vs CountDownLatch
小白常常搞混这两个工具,它们虽然都用于线程协调,但有明显区别:

在实际应用场景中:
-
CyclicBarrier 适合多阶段同步场景,如 MapReduce 中每个阶段结束都需要同步一次,或游戏中每局结束后准备下一局
-
CountDownLatch 适合单阶段等待场景,如主线程等待多个子线程完成初始化,或等待多个服务启动完成
-
CyclicBarrier 像是"人到齐才能开会",也就是所有参与者必须同时到达指定地点,缺一不可,且会议结束后下次还能再用同样的会议室开会
-
CountDownLatch 更像是"火箭发射倒计时",其中主线程调用
await()
等待,而多个子线程各自完成工作后调用countDown()
减少计数,且倒计时结束后就不能重复使用
CyclicBarrier vs Phaser
Phaser 是 Java 7 引入的更灵活的同步工具,可以看作 CyclicBarrier 的增强版。下表展示了两者的核心差异:
特性 | CyclicBarrier | Phaser |
---|---|---|
参与线程数 | 固定(构造时指定) | 动态(可注册/注销线程) |
阶段同步 | 每个阶段需固定线程数参与 | 支持动态调整阶段参与线程数 |
屏障动作 | 单一线程执行(最后到达者) | 可由任意线程触发(通过arrive() ) |
适用场景 | 固定线程数的多阶段同步 | 任务树分解、动态并行任务 |
选择时机:当任务规模可能变化或线程参与情况不固定时,考虑使用 Phaser;当架构简单且参与线程数固定时,CyclicBarrier 通常更加高效。
CyclicBarrier 的核心 API
先看看 CyclicBarrier 提供的主要方法:
java
// 构造方法,parties指定参与线程数
public CyclicBarrier(int parties)
// 构造方法,增加了屏障被打破时要执行的动作
public CyclicBarrier(int parties, Runnable barrierAction)
// 等待其他线程到达屏障点
public int await() throws InterruptedException, BrokenBarrierException
// 指定超时时间的等待
public int await(long timeout, TimeUnit unit)
throws InterruptedException, BrokenBarrierException, TimeoutException
// 重置屏障到初始状态
public void reset()
// 查询当前在屏障处等待的线程数
public int getNumberWaiting()
// 查询此屏障是否处于损坏状态
public boolean isBroken()
实战案例:游戏大厅等待机制
想象一个多人游戏的匹配场景,只有当 4 名玩家都准备好后,游戏才能开始:
java
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ThreadLocalRandom;
public class GameLobbyDemo {
private static final int PLAYER_COUNT = 4;
public static void main(String[] args) {
// 创建CyclicBarrier,当4个玩家都准备好时,执行开始游戏的逻辑
CyclicBarrier barrier = new CyclicBarrier(PLAYER_COUNT, () -> {
System.out.println("所有玩家已就绪,游戏开始!");
System.out.println("====================");
});
// 模拟4个玩家加入游戏(每个玩家对应一个工作线程)
for (int i = 1; i <= PLAYER_COUNT; i++) {
final int playerId = i;
new Thread(() -> {
try {
// 模拟玩家加载资源,需要随机时间
System.out.println("玩家" + playerId + "正在加载游戏资源...");
int loadingTime = ThreadLocalRandom.current().nextInt(1000, 5000);
Thread.sleep(loadingTime);
System.out.println("玩家" + playerId + "已准备就绪,等待其他玩家... (耗时:" + loadingTime + "ms)");
// 在屏障点等待其他玩家
barrier.await();
// 所有玩家都准备好后,开始游戏
System.out.println("玩家" + playerId + "进入游戏世界");
// 游戏结束后,可以重用CyclicBarrier进行下一局
System.out.println("玩家" + playerId + "本局游戏结束,准备下一局");
// 模拟短暂休息
Thread.sleep(ThreadLocalRandom.current().nextInt(500, 1500));
// 重新等待其他玩家准备下一局
System.out.println("玩家" + playerId + "已准备好下一局,等待其他玩家...");
barrier.await();
System.out.println("玩家" + playerId + "开始新一局游戏");
} catch (InterruptedException | BrokenBarrierException e) {
System.err.println("玩家" + playerId + "遇到问题,退出游戏");
e.printStackTrace(); // 打印详细异常堆栈,方便排查问题
}
}, "游戏线程-" + i).start();
}
}
}
运行结果会显示玩家以不同速度准备就绪,然后在屏障点等待其他玩家,所有玩家都准备好后才会一起开始游戏。更重要的是,游戏结束后,我们可以重用同一个 CyclicBarrier 开始新一局!
深入案例:分阶段并行计算
除了游戏大厅等待机制,CyclicBarrier 还可以应用于分阶段并行计算的场景。假设我们要对一个大数组进行多阶段处理,每个阶段都需要等待所有工作线程完成当前阶段才能进入下一阶段:
java
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.Arrays;
public class ParallelArrayProcessor {
private static final int THREAD_COUNT = 3;
private static final int ARRAY_SIZE = 10; // 故意设置为不能被线程数整除
private static int[] dataArray = new int[ARRAY_SIZE];
// 预期的阶段性结果,用于验证处理正确性
private static final int[] EXPECTED_STAGE1 = new int[ARRAY_SIZE]; // 全部为2
private static final int[] EXPECTED_STAGE2 = new int[ARRAY_SIZE]; // 全部为5
private static final int[] EXPECTED_STAGE3 = new int[ARRAY_SIZE]; // 全部为2
static {
// 初始化预期结果数组
Arrays.fill(EXPECTED_STAGE1, 2); // 1*2=2
Arrays.fill(EXPECTED_STAGE2, 5); // 2+3=5
Arrays.fill(EXPECTED_STAGE3, 2); // 5/2=2 (整数除法)
}
public static void main(String[] args) {
// 初始化数组
Arrays.fill(dataArray, 1);
System.out.println("初始数组: " + Arrays.toString(dataArray));
// 创建屏障,所有线程到达后验证结果并打印当前状态
CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT, () -> {
System.out.println("==当前阶段完成,数组状态: " + Arrays.toString(dataArray) + "==");
// 验证阶段结果
if (Arrays.equals(dataArray, EXPECTED_STAGE1)) {
System.out.println("√ 第一阶段结果验证成功");
} else if (Arrays.equals(dataArray, EXPECTED_STAGE2)) {
System.out.println("√ 第二阶段结果验证成功");
} else if (Arrays.equals(dataArray, EXPECTED_STAGE3)) {
System.out.println("√ 第三阶段结果验证成功");
}
// 注意:这里的验证代码是轻量级的,在实际应用中,
// 屏障动作应保持简短,避免阻塞其他线程
});
// 启动工作线程
for (int t = 0; t < THREAD_COUNT; t++) {
final int threadNum = t;
new Thread(() -> {
try {
// 计算每个线程负责处理的数组部分
int perThreadBaseSize = ARRAY_SIZE / THREAD_COUNT; // 每个线程基本处理元素数
int extraElements = ARRAY_SIZE % THREAD_COUNT; // 剩余需分配的额外元素
// 计算起始索引,前extraElements个线程各多处理1个元素
int start = threadNum * perThreadBaseSize + Math.min(threadNum, extraElements);
// 计算结束索引
int end;
if (threadNum < extraElements) {
// 前extraElements个线程多处理一个元素
end = start + perThreadBaseSize + 1;
} else {
end = start + perThreadBaseSize;
}
// 第一阶段:每个元素乘以2
// 这个阶段模拟数据的初步放大处理
for (int i = start; i < end; i++) {
dataArray[i] *= 2; // 数据放大,值变为2
Thread.sleep(100); // 模拟计算耗时
}
System.out.println("线程" + threadNum + "完成第一阶段" +
",处理范围[" + start + "," + end + ")");
// 等待所有线程完成第一阶段
barrier.await();
// 第二阶段:每个元素加3
// 这个阶段模拟数据的偏移处理
for (int i = start; i < end; i++) {
dataArray[i] += 3; // 数据偏移,值变为5
Thread.sleep(100); // 模拟计算耗时
}
System.out.println("线程" + threadNum + "完成第二阶段" +
",处理范围[" + start + "," + end + ")");
// 等待所有线程完成第二阶段
barrier.await();
// 第三阶段:每个元素除以2
// 这个阶段模拟数据的归一化处理
for (int i = start; i < end; i++) {
dataArray[i] /= 2; // 数据归一化,值变为2.5,因为是int所以结果是2
Thread.sleep(100); // 模拟计算耗时
}
System.out.println("线程" + threadNum + "完成第三阶段" +
",处理范围[" + start + "," + end + ")");
// 等待所有线程完成第三阶段
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
System.err.println("线程" + threadNum + "处理出错:" + e.getMessage());
e.printStackTrace();
}
}, "计算线程-" + t).start();
}
}
}
这个例子展示了如何使用 CyclicBarrier 来协调多个线程对同一数据结构的分阶段处理。每个线程负责处理数组的一部分,在每个阶段结束时,所有线程都会在屏障点等待,确保所有部分都处理完毕后再一起进入下一阶段。特别注意的是,当数组大小不能被线程数整除时,我们采用了"前几个线程多处理一个元素"的分配策略。
实际案例:模拟银行柜台服务
再看一个更贴近实际的例子,模拟银行服务流程,客户需要依次经过填表、审核、办理、存档四个环节:
java
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ThreadLocalRandom;
public class BankServiceSimulation {
private static final int CUSTOMER_COUNT = 5; // 客户数量
private static final int STEP_COUNT = 4; // 服务步骤数量
private static final String[] STEPS = {
"填写表格", "材料审核", "业务办理", "资料存档"
};
// 将客户处理每个步骤的逻辑提取成一个单独的方法,提高代码复用性
private static void processStep(int customerId, int step, CyclicBarrier barrier)
throws InterruptedException, BrokenBarrierException {
// 随机处理时间,模拟不同客户在不同环节的耗时差异
int processingTime = ThreadLocalRandom.current().nextInt(500, 2000);
Thread.sleep(processingTime);
System.out.println("客户" + customerId + "完成" + STEPS[step] +
",用时" + processingTime + "ms");
// 正确计算已到达和剩余线程数
int arrived = CUSTOMER_COUNT - barrier.getNumberWaiting(); // 已到达线程数
int remaining = barrier.getNumberWaiting(); // 剩余未到达线程数
System.out.println("客户" + customerId + "等待其他人,当前已有" + arrived +
"人完成,还有" + remaining + "人未完成" + STEPS[step]);
// 等待所有客户完成当前环节
barrier.await();
}
public static void main(String[] args) {
// 为每个步骤创建一个CyclicBarrier
CyclicBarrier[] barriers = new CyclicBarrier[STEP_COUNT];
for (int i = 0; i < STEP_COUNT; i++) {
final int stepIndex = i;
barriers[i] = new CyclicBarrier(CUSTOMER_COUNT, () -> {
System.out.println("\n===== 所有客户已完成环节:" + STEPS[stepIndex] + " =====\n");
// 模拟工作人员准备下一环节
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("工作人员准备被中断:" + e.getMessage());
}
if (stepIndex < STEP_COUNT - 1) {
System.out.println("工作人员准备开始下一环节:" + STEPS[stepIndex + 1]);
} else {
System.out.println("银行工作日结束!");
}
});
}
// 创建客户线程,每个客户由一个独立工作线程表示
for (int i = 1; i <= CUSTOMER_COUNT; i++) {
final int customerId = i;
new Thread(() -> {
try {
// 使用提取的方法处理每个步骤
for (int step = 0; step < STEP_COUNT; step++) {
processStep(customerId, step, barriers[step]);
}
System.out.println("客户" + customerId + "办完所有业务,满意离开");
} catch (InterruptedException e) {
System.err.println("客户" + customerId + "被叫去做别的事情,中断办理:" + e.getMessage());
e.printStackTrace();
} catch (BrokenBarrierException e) {
System.err.println("客户" + customerId + "遇到系统问题,无法继续办理:" + e.getMessage());
e.printStackTrace();
}
}, "银行线程-" + i).start();
}
}
}
这个例子展示了如何通过代码模块化提高可读性和可维护性,同时更能体现 CyclicBarrier 在多阶段同步场景中的应用,每个阶段都需要所有线程到达后才能共同进入下一阶段。
真实应用场景
CyclicBarrier 在实际项目中有许多应用,以下是几个真实技术栈中的应用案例:
Spring Batch 中的并行处理
在 Spring Batch 框架中,当需要并行处理数据切片时,可以使用 CyclicBarrier 确保所有工作线程完成当前批次处理后再进行批次提交:
java
// 伪代码示例
public class ParallelItemProcessor implements ItemProcessor<InputData, OutputData> {
private final CyclicBarrier barrier;
private final List<Worker> workers;
@Override
public OutputData process(InputData item) throws Exception {
// 分派任务给工作线程
distributeToWorkers(item);
// 等待所有工作线程完成处理
barrier.await();
// 合并结果
return mergeResults();
}
}
Kafka 消费者的批处理同步
在 Kafka 消费者组实现中,当多个线程并行处理消息时,使用 CyclicBarrier 可以确保所有消息处理完成后再提交偏移量:
java
// 伪代码示例
public class BatchKafkaConsumer {
private final CyclicBarrier processBarrier;
public void consumeBatch(List<ConsumerRecord<K, V>> records) {
// 将记录分配给多个工作线程处理
for (int i = 0; i < threadCount; i++) {
final int threadId = i;
executor.submit(() -> {
try {
processRecordsSlice(recordSlices.get(threadId));
// 等待所有线程完成处理
processBarrier.await();
} catch (Exception e) {
// 处理异常
}
});
}
// 主线程也等待,确保在提交偏移量前所有处理都完成
processBarrier.await();
consumer.commitSync();
}
}
性能测试中的"同步发车"
在性能测试中,我们常需要模拟大量用户同时发起请求,CyclicBarrier 正是实现这种"同步发车"的理想工具:
java
public class LoadTester {
public void runTest(int threadCount, int requestsPerThread) {
CyclicBarrier startBarrier = new CyclicBarrier(threadCount + 1); // +1 包括主线程
CountDownLatch finishLatch = new CountDownLatch(threadCount);
// 创建并启动测试线程
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
// 等待所有线程就绪
startBarrier.await();
// 执行测试请求
for (int j = 0; j < requestsPerThread; j++) {
executeRequest();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
finishLatch.countDown();
}
}).start();
}
try {
// 主线程解除屏障,所有测试线程同时开始
startBarrier.await();
System.out.println("所有测试线程同时启动!");
// 等待所有测试线程完成
finishLatch.await();
System.out.println("测试完成!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
常见问题及解决方案
问题 1:CyclicBarrier.await()抛出 BrokenBarrierException
问题分析: 当 CyclicBarrier 处于"损坏"状态时,调用 await()会抛出 BrokenBarrierException。这可能发生在:
- 某个等待的线程被中断
- 调用 reset()方法重置屏障
- 屏障动作执行时抛出异常
解决方案:
java
try {
barrier.await();
} catch (BrokenBarrierException e) {
if (barrier.isBroken()) {
System.err.println("屏障已损坏,尝试恢复,原因:" + e.getMessage());
// 记录详细日志,便于问题排查
e.printStackTrace();
// 根据应用场景决定重置屏障
barrier.reset();
// 根据业务逻辑决定是重试还是放弃
boolean shouldRetry = determineIfShouldRetry();
if (shouldRetry) {
try {
barrier.await();
} catch (Exception ex) {
// 重试失败处理
System.err.println("重试失败,放弃本次操作");
}
}
}
}
问题 2:线程永久等待
问题分析: 如果参与线程数量设置错误,或某些线程未能达到屏障点(比如由于异常退出),其他线程可能永久等待。这是多线程开发中常见的死锁问题之一。
解决方案:
java
try {
// 最多等待10秒,防止无限等待
boolean result = barrier.await(10, TimeUnit.SECONDS) >= 0;
if (result) {
System.out.println("所有线程都已到达屏障点,继续执行");
} else {
System.err.println("等待超时,可能有线程未到达或出现异常");
}
} catch (TimeoutException e) {
System.err.println("等待超时,执行超时处理逻辑:" + e.getMessage());
// 记录超时线程信息,方便排查问题
Thread currentThread = Thread.currentThread();
System.err.println("超时线程:" + currentThread.getName() + ", 状态:" + currentThread.getState());
// 执行超时处理逻辑,比如释放资源、记录日志等
handleTimeout();
}
问题 3:性能和死锁问题
问题分析: 不当使用 CyclicBarrier 可能导致性能瓶颈或死锁。主要原因包括:
- 屏障动作执行时间过长
- 屏障内部循环依赖
- 参与线程数设置不当
解决方案:
- 避免在屏障动作中执行耗时操作,将耗时操作异步化
- 不要在 barrierAction 中调用同一个 CyclicBarrier 的 await()方法,避免死锁
- 合理设置参与线程数量,确保与实际等待线程数匹配
- 考虑使用线程池管理线程,避免线程数量失控
java
// 错误示例:在屏障动作中调用自身await(),导致死锁
CyclicBarrier deadlockBarrier = new CyclicBarrier(2, () -> {
try {
// 在屏障动作中又调用await(),会导致死锁
deadlockBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
});
// 推荐方式:屏障动作简短,耗时操作异步处理
CyclicBarrier goodBarrier = new CyclicBarrier(2, () -> {
// 简短的操作,不调用await()
System.out.println("所有线程到达屏障点,执行简单的汇总操作");
// 如果有耗时操作,放在单独的线程中异步执行
new Thread(() -> {
try {
performTimeConsumingTask();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
});

CyclicBarrier 实现原理简化分析
CyclicBarrier 的工作流程可以分解为以下步骤:

CyclicBarrier 内部主要依赖 ReentrantLock 和 Condition 实现:

以下是 CyclicBarrier 的简化实现原理示例代码:
java
// 这是一个简化的CyclicBarrier实现原理示例,不是实际源码
public class SimplifiedCyclicBarrier {
// 重入锁,用于线程同步
// 选择ReentrantLock而非synchronized的原因:
// 1. 提供更灵活的锁操作,如可中断锁、超时锁、公平锁
// 2. 支持多个条件变量(Condition),实现精准线程唤醒
// 3. 相比synchronized+wait/notify的复杂控制逻辑,代码更清晰
private final ReentrantLock lock = new ReentrantLock();
// 条件变量,用于线程等待和唤醒,支持更精细的线程控制
private final Condition trip = lock.newCondition();
// 参与线程数
private final int parties;
// 当前等待的线程数
private int count;
// 所有线程到达屏障时执行的动作
// 由最后一个到达的线程执行,天然线程安全,无需额外同步
private final Runnable barrierCommand;
// 表示当前屏障的"代"
// 在JDK实现中,这个引用是volatile的,确保多线程环境下的内存可见性
// volatile保证所有线程看到的generation引用都是最新的,避免脏读问题
private Generation generation = new Generation();
// 私有静态内部类,表示屏障的一代
// 用Generation对象而不是简单的boolean可以避免虚假唤醒和ABA问题
private static class Generation {
// 标记此代屏障是否被破坏
boolean broken = false;
}
public SimplifiedCyclicBarrier(int parties, Runnable barrierAction) {
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
// 等待其他线程到达屏障点
public int await() throws InterruptedException, BrokenBarrierException {
// 获取锁
lock.lock();
try {
// 获取当前代,用于标识这一轮的屏障
final Generation g = generation;
// 如果当前代已被破坏,抛出异常
if (g.broken) {
throw new BrokenBarrierException();
}
// 如果线程被中断,破坏当前代并抛出异常
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
// 剩余等待线程数减1
int index = --count;
// 如果是最后一个到达的线程
if (index == 0) {
boolean ranAction = false;
try {
// 执行屏障动作
if (barrierCommand != null)
barrierCommand.run();
ranAction = true;
// 重置屏障,开始新一代
nextGeneration();
return 0;
} finally {
// 如果执行barrierCommand失败,破坏屏障
if (!ranAction)
breakBarrier();
}
}
// 如果不是最后一个到达的线程,则等待
for (;;) {
try {
// 在条件变量上等待
// 此处可能发生"虚假唤醒",即线程可能在没有被明确通知的情况下从await()返回
trip.await();
} catch (InterruptedException ie) {
// 如果等待被中断,检查状态
if (g == generation && !g.broken) {
breakBarrier();
throw ie;
} else {
// 如果屏障已经重置或被破坏,只是重新中断当前线程
Thread.currentThread().interrupt();
}
}
// 检查屏障是否被破坏
if (g.broken)
throw new BrokenBarrierException();
// 通过对比generation引用,确保唤醒的线程属于当前有效屏障周期
// 避免"虚假唤醒"导致的问题,只有同一generation内的唤醒信号才有效
if (g != generation)
return index;
}
} finally {
// 释放锁
lock.unlock();
}
}
// 破坏屏障,唤醒所有等待的线程
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
// 重置屏障,开始新一代
private void nextGeneration() {
// 唤醒此代所有等待的线程
trip.signalAll();
// 重置count
count = parties;
// 创建新一代,这一步非常关键
// 新的Generation引用确保await()方法中的代检查可以区分不同轮次
generation = new Generation();
}
}
这个简化的实现展示了 CyclicBarrier 的核心工作原理:
- 使用 ReentrantLock 而非 synchronized,提供更灵活的锁操作和精准的条件控制
- 初始化时,count 值设为参与线程数(parties)
- 每个线程调用 await()时,count 减 1
- 如果 count 不为 0,当前线程在 Condition 上等待
- 如果 count 为 0,执行屏障动作,重置 count,并唤醒所有等待线程
- Generation 对象用于标识不同的屏障周期,每次重置都创建新 Generation,通过引用比较避免虚假唤醒
使用场景总结
CyclicBarrier 非常适合以下场景:
- 并行迭代算法:将大任务拆分成多个子任务,每轮迭代结束后同步一次
- 多阶段并行计算:计算分为多个阶段,每个阶段都需要使用上一阶段的结果
- 游戏同步:等待所有玩家加载完成后开始游戏
- 分布式系统中的数据一致性:等待所有节点数据同步后进行下一步操作
- 模拟测试:控制多个线程同时发起请求,测试系统性能
实用技巧和常见问题解答
实用技巧
- 屏障动作保持轻量:屏障动作由最后一个到达的线程执行,耗时操作应异步处理
- 总是使用带超时的 await():避免由于线程异常导致的永久等待
- 重置前确保线程已释放:不要在仍有线程在等待时调用 reset()
常见面试问题解答
Q: CyclicBarrier 如何实现可重用?
A: CyclicBarrier 通过在最后一个线程到达时创建新的 Generation 对象,并重置计数器来实现可重用。这使得它能在一轮同步完成后自动准备下一轮同步,无需手动重置。
Q: 屏障损坏后如何恢复?
A: 调用reset()
方法可以恢复损坏的屏障。这会创建一个新的 Generation 对象,重置计数器,并唤醒所有等待的线程。但注意,等待中的线程会收到 BrokenBarrierException。
Q: 为什么 CyclicBarrier 构造函数需要指定参与线程数?
A: 指定参与线程数是为了让 CyclicBarrier 知道需要等待多少个线程到达。这个数字必须精确,否则可能导致永久等待(如果实际线程数少于指定数)或提前释放(如果还有线程未到达就已触发)。
总结
特性 | 说明 |
---|---|
核心功能 | 允许多个线程互相等待,直到所有线程都到达某个点 |
重用性 | 可以被重置并重复使用,适合迭代或周期性任务 |
屏障动作 | 支持在所有线程到达屏障时执行预定义的操作 |
线程安全 | 屏障动作在单线程中执行,天然线程安全 |
异常处理 | 通过 BrokenBarrierException 和 isBroken()方法处理异常情况 |
使用场景 | 多阶段计算、并行算法、游戏同步、分布式系统协调等 |
性能考虑 | 需注意 parties 设置、屏障动作耗时、异常处理等问题 |
CyclicBarrier 是 Java 并发工具包中一个非常实用的同步工具,掌握它能让你在多线程协作场景中事半功倍。它不仅能确保多个线程步调一致地执行,还能通过屏障动作在同步点执行额外的处理逻辑。