Java Concurrent Hashmap initTable() 为什么 try/finally 块?

Java Concurrent Hashmap initTable() Why the try/finally block?

我一直在看下面的代码(来自here)'

/**
 * 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.compareAndSetInt(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;
}

有人可以解释为什么需要 try 块吗?

未经检查的异常和错误作为一个概念存在。如果您在 ConcurrentHashMap 上调用 .put(),并且您的内存非常紧张,则该映射可能会尝试创建一个数组,并且该调用可能会因 OutOfMemoryError 而失败。代码仍将继续(可能在捕获此代码的 catch 块处),并且对该映射的引用仍然存在。如果发生这种情况后,地图崩溃并完全失效,因为 sizeCtl 的值已损坏,那就有点糟糕了。

重点是 finally 块,它恢复了 sizeCtl 值。这用于各种事情,值得注意的包括管理哪个线程获得访问权限。如果没有,任何其他看跌期权将永远旋转。

这方面的相关性(例如,此处可抛出事件发生的频率与删除 'try' 和 'finally' 并以 sizeCtl = sc; 结尾而没有额外开销try/finally) 低,但如果相关,则非常相关。

它是一种互斥体 - 也是一种 double-check 锁。

首先

if ((sc = sizeCtl) < 0)
    Thread.yield(); // lost initialization race; just spin

检查 sizeCtl 互斥体。如果小于0,说明已经有人抢了,稍等一下。

如果它是零(或更多),我们很可能会获得锁。所以执行了Compare-And-Swap:

if (U.compareAndSetInt(this, SIZECTL, sc, -1)

这是一个原子操作 - 如果另一个线程在第一次和第二次检查之间抓住它,if 将不会被采取。

如果 CAS 成功,则该方法负责释放互斥量。因此,使用 try-finally 来确保释放互斥锁(sizeCtl 重置为其原始值),而不管是否发生异常(例如,分配新节点时内存不足 - 或者是否存在适用于地图的唯一限制)或不适用。