sizeCtl 是 ConcurrentHashMap 中兼具状态信号量与阈值缓存功能的 volatile int 字段;初始化时 CAS 设为 -1 表示抢占初始化权,0 表示未初始化或扩容阈值,-1 表示正初始化。

sizeCtl 是 ConcurrentHashMap 里一个看似简单、实则承载多重职责的 volatile int 字段。它不是单纯的“容量”或“大小”,而是一个状态信号量 + 阈值缓存的复合角色,所有结构变更(初始化、扩容)都必须先与它协调。
初始化阶段:用负数抢占控制权
首次 put 时若 table 为空,会调用 initTable():
- CAS 尝试把 sizeCtl 从 0(或用户指定的正数)设为 -1;成功者获得初始化权限
- 失败线程发现 sizeCtl
- 初始化完成后,sizeCtl 被设为新数组长度 × 0.75(如容量 16 → sizeCtl = 12)
扩容触发与协作:负数值编码线程参与数
当元素数量超过当前 sizeCtl 且 table 非空,扩容被触发:
- 首个线程 CAS 将 sizeCtl 从正数(如 12)改为一个负数扩容戳,形如 -(1 + 扩容线程数)
- 例如 sizeCtl = -3,表示已有 2 个线程正在协助迁移数据
- 每个新加入的扩容线程,都会对 sizeCtl 做一次 CAS 减 1 操作(即 -3 → -4)
- 迁移完毕后,sizeCtl 被重置为新表的扩容阈值(如新容量 32 → sizeCtl = 24)
不同取值对应的状态语义
sizeCtl 的数值不是随意设定,每种范围都有明确含义:
- sizeCtl == 0:默认初始状态,table 未创建,下一次 put 将触发初始化
- sizeCtl > 0:两种可能——未初始化时是用户指定的初始容量;已初始化后是当前扩容阈值(table.length × 0.75)
- sizeCtl == -1:有线程正在执行初始化,其他线程需等待
- sizeCtl :扩容进行中,绝对值减 1 即为当前参与扩容的线程数
它不等于实际元素个数,也不等于数组长度
容易混淆的是,sizeCtl 和实际 size 完全无关:
- 真实元素总数由 baseCount + CounterCell[] 共同累加得出,sizeCtl 不参与计数
- 它也不等于 table.length,而是该长度乘以负载因子后的阈值(初始化后)或扩容过程中的状态标记(负值)
- 它的核心价值在于:用一个整型字段,无锁地表达“谁在干活、干到哪一步、下一步该谁上”,避免竞争破坏一致性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











