WorkQueue是线程私有、无锁、基于固定长度2的幂次Object[]数组实现的双端队列,通过volatile字段base和top以环形缓冲区方式管理任务,top-base为任务数,索引用位运算取模。

每个 ForkJoinWorkerThread 内部绑定的 WorkQueue 是一个**线程私有、无锁、基于数组实现的双端队列**,其物理结构由 JVM 堆中的一段连续对象数组承载,关键在于理解它的内存布局与访问约束。
WorkQueue 的底层数组是固定长度的 Object[]
WorkQueue 类中核心字段是 Array(实际为 final ForkJoinTask>[] array),该数组在首次入队时通过 U.allocateInstance(WorkQueue.class) + 手动初始化创建,长度恒为 2 的幂次(如 1024、2048),由 ForkJoinPool 的 config 参数隐式控制(默认最小为 1024)。它不是动态扩容的 ArrayList,而是类似环形缓冲区的静态结构:
- 数组地址在堆中连续分配,元素类型为
ForkJoinTask>,每个槽位存储一个任务引用 - 不存元数据(如 size 字段),靠
base(出队索引)和top(入队索引)两个 volatile int 字段维护逻辑位置 - 索引通过位运算取模:
array[(index & (array.length - 1))],要求长度必须是 2 的幂
base 和 top 共同定义“有效区间”,非传统首尾指针
不同于普通双端队列,WorkQueue 的 base 指向**下一个待窃取/执行的任务位置**,top 指向**下一个待 push 的空槽位**。二者均为从 0 开始单调递增的 long 值(实际用 int 模拟,但语义上可溢出),其差值 top - base 表示当前队列中任务数量。物理数组中真正被占用的区域是:
- 若
top >= base:有效任务位于[base % length, (top - 1) % length]区间(左闭右闭) - 若
top (发生绕回):任务分布在两段——<code>[0, (top - 1) % length]和[base % length, length - 1] - 数组本身始终满容量存在,未使用的槽位为
null引用,不触发 GC 压力
线程绑定与内存可见性靠 UNSAFE + volatile 保障
每个 WorkQueue 实例在创建后即通过 ForkJoinWorkerThread.workQueue 字段强引用绑定,且该字段声明为 volatile。所有对 array 元素的读写均使用 UNSAFE.putObjectVolatile / getObjectVolatile,确保:
- 同一线程内对
top的递增(push)与对base的递增(pop)不会被重排序 - 其他线程(窃取者)能立即看到
top的最新值,从而判断是否可窃取 - 数组引用本身不可变,但内容通过 volatile 语义保证跨线程可见
可通过 Unsafe.addressOf 或 JOL 验证物理连续性
若需实证其物理结构,可用以下方式观察:
- 用 JOL(Java Object Layout) 打印
WorkQueue实例:`GraphLayout.parseInstance(queue).toPrintable()`,确认array字段偏移及数组对象头+数据体的连续内存块 - 在调试模式下,通过 HotSpot 调试器或
Unsafe获取数组起始地址:unsafe.arrayBaseOffset(Object[].class)+objectFieldOffset("array"),再结合arrayLength推算总跨度 - 注意:JVM 可能因压缩指针(-XX:+UseCompressedOops)使对象头与数组数据体之间存在 padding,但数组本体仍是连续的
Object*序列

















