我正在阅读jdk1.8中ConcurrentHashMap的源代码,我发现initalTable()
方法有点令人困惑。
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable {
//some fields revolved in this method
transient volatile Node<K,V>[] table;
private transient volatile int 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)) {
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];//place 1
table = tab = nt;//place 2
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
}
主要的困惑点集中在以下几个方面:
1. 为什么我们需要
while((tab = table) == null || tab.length == 0)
来检查table
不是null且其长度不为0,并确定是否继续循环,因此只有当这两个条件都为false时才退出循环。我无法想象出table
不为null但长度为0的情况,因为它是在else if
块中初始化的,而n始终被赋一个大于0的值。那么为什么我们需要两个条件而不是只需要其中一个?
更新: 我忘了一件事,即一次成功的CAS操作将sizeCtl
交换为-1,这将阻止其他线程进入else if
块,但我仍然不清楚为什么需要在if
和while
中使用两个条件。希望有人能为我澄清一下。谢谢。
sizeCtl
已经交换为-1,因此会阻止其他线程进入该块,对吧?但第一点呢?您能否请解释一下? - Boyu Zhang