CyclicBarrier基于AQS实现但不直接继承,其核心是私有静态类Sync(继承AQS)管理计数与等待,配合Generation实现代际隔离;Sync用state表示剩余线程数,手动遍历队列唤醒,支持循环重置与中断处理。

CyclicBarrier 的核心同步逻辑确实基于 AQS(AbstractQueuedSynchronizer),但它**并不是直接继承或组合 AQS 类**,而是**内部使用了一个私有的、轻量级的 AQS 子类——Generation 配合一个独立的、基于 AQS 的同步器(即 Sync)来实现循环等待与重置能力**。准确地说:CyclicBarrier 内部定义了一个叫 Sync 的私有静态类,它继承自 AbstractQueuedSynchronizer,并复用 AQS 的 state 字段和等待队列机制,完成线程阻塞/唤醒与计数协调。
Sync 类如何复用 AQS 实现屏障计数与等待
Sync 是 CyclicBarrier 的“同步引擎”:
- 它把 AQS 的
state字段直接用作**剩余等待线程数**(初始为 parties,每次await()成功就state--) - 调用
await()时,线程尝试执行tryAcquireShared(1)—— 实际是检查state > 0;若未满员,就走doAcquireSharedInterruptibly()进入 AQS 等待队列(挂起) - 当最后一个线程调用
await(),state减为 0,此时触发nextGeneration():唤醒所有等待线程,并重置state = parties,同时翻转broken标志和递增generation版本号 - 唤醒逻辑不靠
release(),而是显式调用LockSupport.unpark()遍历 AQS 队列中的节点(AQS 的doReleaseShared()不适用,因为 barrier 不是独占/共享语义的标准用法,CyclicBarrier 自行管理唤醒)
为什么不用标准共享模式?关键在“循环”与“中断感知”
AQS 的共享模式(如 CountDownLatch)只支持一次释放,无法重置;而 CyclicBarrier 必须支持多次重复使用。为此:
-
Sync重写了tryAcquireShared和tryReleaseShared,但tryReleaseShared只在最后线程到达时被调用一次,用于触发换代,而非常规释放 - 它不依赖 AQS 的“传播唤醒”机制(
setHeadAndPropagate),而是手动遍历 wait queue 唤醒所有线程,确保每个 await 调用者都能收到通知 - 对中断敏感:
await()检测到中断会设置broken = true,并唤醒所有已等待线程(抛出BrokenBarrierException),这个清理动作也由Sync在breakBarrier()中完成,同样绕过标准 AQS 流程
Generation 作用:隔离每一轮等待,避免虚假唤醒
Generation 是一个极简的标记类(仅含一个 boolean broken 字段),配合 Sync 中的 generation 引用,实现“代际隔离”:
立即学习“Java免费学习笔记(深入)”;
- 每个新屏障周期(即一次完整等待+释放)对应一个新
Generation实例 - 线程在 park 前会记录当前
generation;被唤醒后先检查g == generation,若不等说明已进入下一轮,直接重试(避免旧轮次唤醒影响新轮次) - 这种设计让
await()可安全响应中断、超时、重置,同时保证多轮之间状态不串扰
小结:AQS 是骨架,CyclicBarrier 是定制血肉
CyclicBarrier 并非“基于 AQS 就等于套用模板”,而是:
- 以 AQS 的
state、CLH 队列、LockSupport工具为基础设施 - 自行实现同步语义:计数归零即触发、手动唤醒全员、代际版本控制、中断即时熔断
- 规避了 AQS 共享锁的单次性限制,通过组合
Sync + Generation实现真正可循环的屏障
所以读源码时重点看 CyclicBarrier.Sync 类 —— 它就是 CyclicBarrier 的 AQS 绑定实现体,也是理解其高性能、可重入、可中断特性的关键入口。


















