一、简介
ConcurrentHashMap在初始化table时调用的方法。方法用private final修饰。
二、源码
先介绍方法内的三个全局变量
- sizeCtl
/**
* 用于控制table初始化和resize的参数
* -1表示初始化
* 其他负数+1表示正在进行resize的线程数,为了与-1区别开
* 0代表默认状态
* 在初始化之后,该值表示下次需要resize时map内元素的个数
*/
private transient volatile int sizeCtl;
- DEFAULT_CAPACITY
/**
* 默认初始化table的容量,必须是2的n次方
*/
private static final int DEFAULT_CAPACITY = 16;
初始化ConcurrentHashMap是传入的容量不是2的n次方时会发生什么?
- SIZECTL
// 初始化时与sizeCtl值相等,为0
private static final long SIZECTL;
static {
try {
U = sun.misc.Unsafe.getUnsafe();
Class<?> k = ConcurrentHashMap.class;
SIZECTL = U.objectFieldOffset
(k.getDeclaredField("sizeCtl"));
} catch (Exception e) {
throw new Error(e);
}
}
下面先贴出initTable()源码
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
可以看出table的初始化在一个cas方法中进行,当table为null或者长度为0时进入while方法。
进入之后判断sizeCtl的指,如果小于0则线程让步。由于初始状态sizeCtl是等于0的,说明前面已经有线程进入了elseif这部分,将sc的值置为-1,表示正在初始化。
如果sc大于0,则取sc,否则取默认容量16。然后计算下一次元素数量达到多少时需要resize。
网友评论