引言:
本文基于JDK1.8版本,而且对ConcurrentHashMap有一定了解的人,本文并非科普该类的用法,而是针对sizeCtl的含义做出纠正
科普文章推荐:
深入浅出ConcurrentHashMap1.8
sizeCtl定义及注释
/**
* Table initialization and resizing control. When negative, the
* table is being initialized or resized: -1 for initialization,
* else -(1 + the number of active resizing threads). Otherwise,
* when table is null, holds the initial table size to use upon
* creation, or 0 for default. After initialization, holds the
* next element count value upon which to resize the table.
*/
private transient volatile int sizeCtl;
sizeCtl 无可置疑是ConcurrentHashMap中一个重要的变量,在各种资料上能看到的基本就是
sizeCtl :默认为0,用来控制table的初始化和扩容操作
-1 代表table正在初始化
-N 表示有N-1个线程正在进行扩容操作
其余情况:
1、如果table未初始化,表示table需要初始化的大小。
2、如果table初始化完成,表示table的容量,默认是table大小的0.75倍
这些资料其实就是按照注释翻译了一遍,但其实注释是有错误的。
-N 表示有N-1个线程正在进行扩容操作,这句话是错误的。
这里-N的定义是有问题的,应该取-N对应的二进制的低16位数值为M,此时有M-1个线程进行扩容。
(感谢评论区的朋友指教)
证明:
能修改sizeCtl的方法有五个:
- initTable()
- addCount()
- tryPresize()
- transfer()
- helpTransfer()
其中initTable是设为-1保证只初始化一次,而且初始化后,把sizeCtl设为长度的0.75倍,此时sizeCtl为正值。
而能把sizeCtl设置为-N的方法只有addCount跟tryPresize方法,而且两个方法的实现逻辑是很相似的,这里选用tryPresize作为讲解。
..省略部分代码
while ((sc = sizeCtl) >= 0) {
ConcurrentHashMap.Node<K,V>[] tab = table; int n;
if (tab == null || (n = tab.length) == 0) {
//表为空,初始化表
// 逻辑和initTable一样,完成后sc也是0.75倍的长度,为正数,继续循环
// 此处省略代码
}
//扩容大小没有达到阈值,或者超过最大容量,无法扩容,直接结束
else if (c <= sc || n >= MAXIMUM_CAPACITY)
break;
else if (tab == table) {
int rs = resizeStamp(n);
//满足while循环条件,证明sc肯定>=0,所以一开始肯定不会满足该判断,而是执行else代码
// 这里的意义其实是已经有线程在进行扩容
if (sc < 0) {
ConcurrentHashMap.Node<K,V>[] nt;
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
if (U.compareAndSwapInt(this, SIZECTL, sc