我正在详细分析Java LongAdder算法。 LongAdder扩展了类Striped64,基本方法是retryUpdate。此方法采用以下代码形式;在链接的源代码中,它占用第212-222行:
try { // Recheck under lock
Cell[] rs; int m, j;
if ( (rs = cells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) {
rs[j] = r;
created = true;
}
} finally {
busy = 0;
}
问题:这个try阻止如何失败?
N.b .:访问权限
rs[j = (m - 1) & h]
不应抛出IndexOutOfBoundsException异常,因为按位与运算的结果始终小于或等于其整数参数的最小值,因此0 <= j <= m-1在数组的范围内。
答案 0 :(得分:2)
这看起来非常像jdk代码本身中其他任何地方与ReentrantLock
一起使用的模式。这里的“模式”是即使发生异常,也应该始终释放锁,因此通常将代码编写为:
Lock someLock...
try {
// use someLock
} finally {
someLock.unlock();
}
由于cellsBusy
(从busy
重命名)实际上是忙碌的锁,因此这里的模式是相同的。因此:
cellsBusy = 0;
实际上是“释放锁”。因此,这并不是真正的失败,而是显式释放锁定。我发现这很容易阅读,并能很好地理解代码。
答案 1 :(得分:2)
由于不推荐使用的Thread.stop
方法是从另一个线程中调用的,因此该代码-以及版本11之前的任何其他Java代码-都可能失败。这可能会随时在目标线程中引发ThreadDeath
错误。但是,线程does at least stay alive足够长,足以执行finally
块。
不推荐使用Thread.stop
方法because this behaviour makes it "inherently unsafe":
为什么不赞成使用Thread.stop?
因为它本质上是不安全的。停止线程会使它解锁它已锁定的所有监视器。 (当ThreadDeath异常在堆栈中传播时,监视器将被解锁。)如果以前由这些监视器保护的任何对象处于不一致状态,则其他线程现在可能会以不一致状态查看这些对象。据说这些物体已损坏。当线程对损坏的对象进行操作时,可能会导致任意行为。此行为可能是微妙的,难以检测,或者可能是明显的。与其他未检查的异常不同,ThreadDeath会无声地杀死线程。因此,用户没有警告其程序可能已损坏。在实际损坏发生后的任何时间,甚至未来数小时或数天,腐败都会显现出来。
从理论上讲,如果在另一个线程中停止了执行该对象的线程,则可以以此方式尝试编写代码来防止将对象置于无效状态。就是说,如果可以随时调用Thread.stop
,则很难保证有效状态,即使尝试这样做也不是很常见的事情,因此这并非作者的意图。 (如果是,则代码中可能会有注释来解释它。)