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
重置为其原始值),而不管是否发生异常(例如,分配新节点时内存不足 - 或者是否存在适用于地图的唯一限制)或不适用。
我一直在看下面的代码(来自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
重置为其原始值),而不管是否发生异常(例如,分配新节点时内存不足 - 或者是否存在适用于地图的唯一限制)或不适用。