sizectl是concurrenthashmap中兼具初始化、扩容控制与并发协调三重职责的核心状态变量;负数表示初始化或扩容中(-1为初始化,-n表n-1线程参与),0表示未初始化,正数为扩容阈值。

SizeCtl 是 ConcurrentHashMap 内部用于协调扩容、初始化和状态控制的关键字段,它**不是单纯的计数器**,而是一个**多功能状态控制变量**,其值的正负和大小承载不同语义。
SizeCtl 的四种核心状态含义
它的取值直接决定当前哈希表所处的阶段:
-
负数(如 -1):表示哈希表正在初始化中(
table == null且线程正在构造初始数组) - 负数(如 -(1 + 扩容线程数)):表示有线程正在执行扩容;例如 -2 表示 1 个线程在扩容,-3 表示 2 个线程参与
- 0:表示使用默认初始容量(16),尚未初始化,也未被显式设置容量
-
正数(≥0):表示下一次触发扩容的阈值(即
capacity * loadFactor),仅在 table 已初始化且无并发操作时有效
SizeCtl 如何参与初始化流程
当首次调用 put 或 initTable 时,会检查 table == null 且 sizeCtl 是否成立:
- 若
sizeCtl == 0,尝试用 CAS 将其设为 -1,成功者获得初始化权限,失败者让出 CPU 等待重试 - 若
sizeCtl (比如已被其他线程设为 -1),说明初始化正在进行,当前线程自旋等待,直到 <code>table != null - 初始化完成后,
sizeCtl被设为扩容阈值(如 12 = 16 × 0.75)
SizeCtl 如何驱动扩容协作
当插入导致元素数超过 sizeCtl 阈值时,会触发扩容:
- 首个发现需扩容的线程尝试将
sizeCtl从正数(如 12)通过 CAS 改为一个负数(如 -(1 + 扩容线程数)),比如 -2 - 后续线程看到
sizeCtl 且 <code>table != null,则协助扩容:帮助迁移某一段桶(transferIndex控制分段),并原子递减扩容线程计数 - 当最后一个线程完成迁移后,将
sizeCtl更新为新容量的扩容阈值(如 32 × 0.75 = 24)
SizeCtl 不是线程安全的“计数器”,而是状态信号量
它不记录当前元素个数(那是 baseCount + CounterCell[] 干的事),也不实时反映桶数量。它的作用是:
- 作为轻量级同步标志(类似锁状态位),避免重复初始化或重复启动扩容
- 隐含传递扩容参与线程数,实现无锁协作
- 在无竞争时缓存扩容阈值,减少重复计算
所有对 sizeCtl 的修改都通过 Unsafe.compareAndSwapInt 完成,保证原子性;读取也常配合 volatile 语义(JDK 8 中为 volatile int)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











