phaser 的 state 用 64 位 long 位域压缩存储 phase(高 32 位)、parties(中 16 位)和 unarrived(低 16 位),通过位移掩码提取、cas 原子更新实现无锁高性能同步。

Java 中 Phaser 的底层状态存储确实高度依赖位运算,它把多个关键字段紧凑地编码在一个 volatile long state 变量中——这是其高性能和无锁设计的核心基础。这种设计避免了额外对象开销和 CAS 冲突放大,但理解它需要拆清每个比特段的职责与协作逻辑。
state 的 64 位布局详解
state 是一个 64 位有符号 long,被划分为三块互不重叠的区域:
-
高 32 位(bit 32–63):存储
phase(当前阶段号),其中最高位(bit 63)是终止标志位(1 表示已终止);其余 31 位(bit 32–62)表示实际 phase 值(从 0 开始,到Integer.MAX_VALUE后回绕) -
中 16 位(bit 16–31):存储
parties(注册的参与者总数),即该 Phaser 当前应有多少线程参与同步 -
低 16 位(bit 0–15):存储
unarrived(尚未到达当前阶段的线程数),初始等于parties,每调用一次arrive()就减 1
对应常量定义(JDK 源码中):
PARTIES_SHIFT = 16 → parties 存在第 16 位起PHASE_SHIFT = 32 → phase 存在第 32 位起UNARRIVED_MASK = 0xffffL(即低 16 位全 1)
如何从 state 提取各字段?
提取完全靠位移 + 掩码,无分支、无对象分配,极轻量:
-
获取 unarrived:
(int)state & UNARRIVED_MASK -
获取 parties:
(int)(state >>> PARTIES_SHIFT) & 0xffff(注意无符号右移>>>避免高位补 1) -
获取 phase:
(int)(state >>> PHASE_SHIFT)(同样无符号右移,高位终止标志不参与 phase 值计算) -
判断是否终止:
(state —— 因为只有 bit 63 为 1 时整个 long 为负数
如何原子更新 state?
典型操作如 doRegister() 中的注册逻辑,需同时更新 parties 和 unarrived:
假设要新增 r 个参与者,则需向 state 加上:adj = ((long)r <br>
即:高位加 <code>r 到 parties 区,低位加 r 到 unarrived 区。
CAS 更新:UNSAFE.compareAndSwapLong(this, stateOffset, s, s + adj)
一次 CAS 完成两个字段的同步变更,避免竞态与锁开销。
phase 推进的触发条件与实现
当 unarrived == 0 时,说明所有注册线程均已到达,此时需推进 phase:
- 将
unarrived重置为parties(为下一阶段准备) - 将
phase加 1(注意处理溢出回绕) - 组合新 state:
next = ((long)(phase + 1)
这个过程由 internalAwaitAdvance() 在最后一个到达者线程中执行,且会回调 onAdvance() —— 此时仍处于 CAS 更新前的临界窗口,保证回调与状态变更的原子性。
这种位域压缩不是炫技,而是让单次 CAS 覆盖全部同步语义,支撑了 Phaser 动态注册、分层树形结构、多阶段复用等高级能力。理解它,就握住了 Phaser 高性能的命脉。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











