在ConcurrentHashMap的put方法中,第一次添加元素的时候table是空的(使用空的构造函数),就会执行initTable方法。
下面就来看一下jdk8是怎么初始化这个table的,如何保证并发不出问题。
关于unsafe.compareAndSwap的用法,可以参见我以前的博客 AtomicInteger分析--自实现无锁并发。
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
//这个有个死循环,
while ((tab = table) == null || tab.length == 0) {
//2.sizeCtl何时为0?看后面
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
//1.这里有个if,如果没进这个if,则继续循环
//什么时候没进这个if呢?
// -就是unsafe.compareAndSwapInt失败的时候,
//什么时候失败呢?
// -当其他的线程也对sizeCtl进行了修改(内存里实际的值与手上拿的不一样,这个时候就返回false了。svn一样)
//2.如果成功了,则进入if,同时,内存中的sizeCtl的值也被修改成了-1,这个时候就可以看到1处做的判断:如果小于0,则放弃此次执行
//3.如果未成功,那继续while:
// -这个时候要么其他的线程已经将table初始化好了,table!=null,直接返回table就行了
// -要么其他线程正在初始化这个表,此时的sizeCtl是-1.所以放弃此次执行。(等待其他线程)
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
//这里就是初始化一个table数组
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设置为新的table的容量(而不是-1)
sizeCtl = sc;
}
//初始化完成了,那就break。
break;
}
}
return tab;
}