ConcurrentHashMap.initTable(),为什么两次检查table是否为空?

ConcurrentHashMap.initTable(), why check table is null twice?

我正在学习java源码,当我阅读ConcurrentHashMap源码时,我对initTable()方法感到困惑,为什么检查(tab = table) == null || tab.length == 0两次,首先在while(),然后在if()。无法想象什么情况下需要二次检查

我想可能是因为 JVM 重新排序代码,把 sizeCtl = sc; 放在 Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; 前面。只是我的猜测,不知道对不对

谁能解释一下,非常感谢

 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;
    }

多个线程可能会竞争执行此操作(请参阅“初始化竞争”评论)。

转述代码:

while(uninitialized) {
    acquire_lock(); //compareAndSwapInt...
    if(uninitialized) {
        do_init();
    }
}

外部检查是一种便宜的“解锁”测试。内部的是万一其他人已经在 whilecompareAndSwapInt 之间成功了。