LongAdder:try 块怎么会失败?
LongAdder: How can the try block fail?
我正在详细分析LongAdder
算法。 LongAdder
扩展了 class Striped64
,其中 class 的基本方法是 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
块怎么会失败?
注意数组访问
rs[j = (m - 1) & h]
不应抛出 IndexOutOfBoundsException
因为按位与运算的结果总是小于或等于其整数参数的最小值,因此 0 <= j <= m-1 在数组的边界。
这与 jdk 代码本身其他地方的 ReentrantLock
使用的模式非常相似。这里的"pattern"是你应该总是释放锁,即使发生Exception,所以通常代码写成:
Lock someLock...
try {
// use someLock
} finally {
someLock.unlock();
}
因为 cellsBusy
(它从 busy
重命名)实际上是一个忙碌的旋转 lock,这里的模式有点相同。因此:
cellsBusy = 0;
实际上是"releasing the lock"。所以这并不是真正的失败,因为它是关于释放锁显式。我发现这更容易阅读和推理代码。
此代码 - 以及版本 11 之前的任何其他 Java 代码 - 可能会因已弃用的 Thread.stop
method being called from another thread. This results in a ThreadDeath
error being thrown in the targeted thread, potentially at any time. However, the thread does at least stay alive 足够长以执行 finally
块而失败。
Thread.stop
方法已弃用 because this behaviour makes it "inherently unsafe":
Why is Thread.stop deprecated?
Because it is inherently unsafe. Stopping a thread causes it to unlock all the monitors that it has locked. (The monitors are unlocked as the ThreadDeath exception propagates up the stack.) If any of the objects previously protected by these monitors were in an inconsistent state, other threads may now view these objects in an inconsistent state. Such objects are said to be damaged. When threads operate on damaged objects, arbitrary behavior can result. This behavior may be subtle and difficult to detect, or it may be pronounced. Unlike other unchecked exceptions, ThreadDeath kills threads silently; thus, the user has no warning that his program may be corrupted. The corruption can manifest itself at any time after the actual damage occurs, even hours or days in the future.
理论上,如果执行对象的线程被另一个线程停止,代码可以这样编写,以防止对象处于无效状态。也就是说,如果 Thread.stop
可以随时调用,则很难保证有效状态,甚至尝试这样做也不是很常见,因此这不太可能是作者的意图。 (如果是,代码可能会有注释解释它。)
我正在详细分析LongAdder
算法。 LongAdder
扩展了 class Striped64
,其中 class 的基本方法是 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
块怎么会失败?
注意数组访问
rs[j = (m - 1) & h]
不应抛出 IndexOutOfBoundsException
因为按位与运算的结果总是小于或等于其整数参数的最小值,因此 0 <= j <= m-1 在数组的边界。
这与 jdk 代码本身其他地方的 ReentrantLock
使用的模式非常相似。这里的"pattern"是你应该总是释放锁,即使发生Exception,所以通常代码写成:
Lock someLock...
try {
// use someLock
} finally {
someLock.unlock();
}
因为 cellsBusy
(它从 busy
重命名)实际上是一个忙碌的旋转 lock,这里的模式有点相同。因此:
cellsBusy = 0;
实际上是"releasing the lock"。所以这并不是真正的失败,因为它是关于释放锁显式。我发现这更容易阅读和推理代码。
此代码 - 以及版本 11 之前的任何其他 Java 代码 - 可能会因已弃用的 Thread.stop
method being called from another thread. This results in a ThreadDeath
error being thrown in the targeted thread, potentially at any time. However, the thread does at least stay alive 足够长以执行 finally
块而失败。
Thread.stop
方法已弃用 because this behaviour makes it "inherently unsafe":
Why is Thread.stop deprecated?
Because it is inherently unsafe. Stopping a thread causes it to unlock all the monitors that it has locked. (The monitors are unlocked as the ThreadDeath exception propagates up the stack.) If any of the objects previously protected by these monitors were in an inconsistent state, other threads may now view these objects in an inconsistent state. Such objects are said to be damaged. When threads operate on damaged objects, arbitrary behavior can result. This behavior may be subtle and difficult to detect, or it may be pronounced. Unlike other unchecked exceptions, ThreadDeath kills threads silently; thus, the user has no warning that his program may be corrupted. The corruption can manifest itself at any time after the actual damage occurs, even hours or days in the future.
理论上,如果执行对象的线程被另一个线程停止,代码可以这样编写,以防止对象处于无效状态。也就是说,如果 Thread.stop
可以随时调用,则很难保证有效状态,甚至尝试这样做也不是很常见,因此这不太可能是作者的意图。 (如果是,代码可能会有注释解释它。)