读ConcurrentHashMap源码有一点疑问。
/**
* Initializes table, using the size recorded in sizeCtl.
*/
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)) {//CAS竞争成功开始初始化
try {
if ((tab = table) == null || tab.length == 0) {//双重检测在这个地方有必要吗
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
重温了一下单例模式的双重检测,发现是有必要的。
假设一个场景,一个新的Map,table为null,两个线程A B同时做put操作,同时进入initTable方法,都发现table为null,然后A线程CAS成功,进入初始化,此时B线程让出CPU时间;A线程初始化结束,这时sizeCtl值位12,然后B线程重新获得CPU时间,由于sizeCtl=12,B线程CAS成功会再次进行初始化。