ConcurrentHashMap扩容:RESIZE_STAMP_BITS等变量与sizeCtl作用机制咨询
ConcurrentHashMap扩容中sizeCtl相关变量的通俗解释
ConcurrentHashMap里的sizeCtl是个"身兼数职"的变量:平时负责控制初始化逻辑和扩容阈值,一旦进入扩容阶段,它就变成了扩容状态的编码容器——把扩容的关键信息打包进一个int类型里。而RESIZE_STAMP_BITS、RESIZE_STAMP_SHIFT、MAX_RESIZERS这三个常量,就是给这个容器的二进制位做分工的规则。
1. RESIZE_STAMP_BITS:扩容批次的"身份码"位数
- 这个常量的值固定是16,意思是把
sizeCtl的高16位划出来,专门存"扩容批次标记"。 - 为啥要这个标记?ConcurrentHashMap可能在不同时机触发扩容(比如第一次从16扩容到32,第二次从32到64),每次扩容的基础场景不一样。用高16位存一个基于当前table长度生成的哈希值(俗称"扩容戳"),就能让线程准确区分自己该参与哪一轮扩容,不会出现"串场"的情况。
- 举个例子:第一次扩容时table长度是16,生成的戳是0xABCD;第二次扩容table长度变32,生成的戳是0xEF12。线程一看
sizeCtl的高16位,就知道自己要加入的是哪批次的扩容。
2. RESIZE_STAMP_SHIFT:扩容标记的"移位工具"
- 它的值是
32 - RESIZE_STAMP_BITS,也就是16。作用是把生成的扩容戳从低16位移到高16位,和后面的计数位彻底分开。 - 比如生成的扩容戳是x,执行
x << RESIZE_STAMP_SHIFT就能把它挪到int的高半区,剩下的低16位就空出来存别的信息。这就像给快递箱贴标签:把"批次号"贴在箱子上半部分,下半部分留着写"当前帮忙的快递员数量"。
3. MAX_RESIZERS:扩容的"最大帮忙人数上限"
- 它的值是
(1 << (32 - RESIZE_STAMP_BITS)) - 1,也就是2^16 - 1 = 65535。这是低16位能表示的最大正整数。 - 因为
sizeCtl的低16位用来记录当前参与扩容的线程数,这个常量就是给扩容队伍设的人数上限——线程太多会抢资源,没必要超过这个数,到顶了就不再允许新线程加入。
sizeCtl和三个变量的协作逻辑
当ConcurrentHashMap触发扩容时,整套机制是这么跑的:
- 初始化扩容状态:先根据当前table长度生成扩容戳,通过
RESIZE_STAMP_SHIFT把戳移到高16位,再把低16位设为1(表示触发扩容的第一个线程已经加入),把这个组合后的值赋值给sizeCtl。 - 线程加入扩容:其他线程检测到
sizeCtl的高16位是有效的扩容戳,就尝试把低16位加1——成功了就正式加入扩容队伍;如果低16位已经到MAX_RESIZERS,就直接放弃参与。 - 线程退出扩容:线程完成自己负责的分片扩容任务后,把
sizeCtl的低16位减1。如果减到0,说明所有扩容线程都干完活了,就把sizeCtl恢复成新的扩容阈值(对应扩容后table的容量*负载因子),扩容正式结束。
举个直观的数值例子:
假设扩容戳是0x1234,左移16位后变成0x12340000,加上初始计数1,sizeCtl就是0x12340001。有线程加入后,sizeCtl变成0x12340002;线程干完活退出,又变回0x12340001。直到最后一个线程退出,sizeCtl变成0x12340000,此时就会把它替换成新的阈值,比如0x00006000(对应扩容后table容量32,负载因子0.75,阈值24)。
内容的提问来源于stack exchange,提问作者ng.newbie
相关产品推荐
相关产品推荐

