[英]LongAdder: How can the try block fail?
我正在详细分析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
块怎么会失败?
注意数组访问
rs[j = (m - 1) & h]
不应抛出IndexOutOfBoundsException
因为按位与运算的结果始终小于或等于其整数参数的最小值,因此 0 <= j <= m-1 在数组的边界内。
这很像在 jdk 代码本身的其他地方与ReentrantLock
使用的模式。 这里的“模式”是你应该总是释放锁,即使发生了异常,所以通常代码写成:
Lock someLock...
try {
// use someLock
} finally {
someLock.unlock();
}
由于cellsBusy
(它从busy
重命名)实际上是一个busy-spin lock ,所以这里的模式有点相同。 像这样:
cellsBusy = 0;
实际上是“释放锁”。 所以这并不是真正的失败,因为它是关于显式释放锁。 我发现这更容易阅读和推理代码。
此代码 - 以及版本 11 之前的任何其他 Java 代码 - 可能会由于从另一个线程调用已弃用的Thread.stop
方法而失败。 这会导致在目标线程中抛出ThreadDeath
错误,可能随时发生。 但是,该线程至少会存活足够长的时间来执行finally
块。
不推荐使用Thread.stop
方法, 因为此行为使其“本质上不安全” :
为什么不推荐使用 Thread.stop?
因为它本质上是不安全的。 停止线程会使其解锁所有已锁定的监视器。 (当 ThreadDeath 异常沿堆栈向上传播时,监视器被解锁。)如果以前受这些监视器保护的任何对象处于不一致状态,则其他线程现在可以查看处于不一致状态的这些对象。 据说此类物品已损坏。 当线程对损坏的对象进行操作时,可能会导致任意行为。 这种行为可能很微妙且难以察觉,也可能很明显。 与其他未经检查的异常不同,ThreadDeath 会默默地杀死线程; 因此,用户没有警告他的程序可能已损坏。 在实际损坏发生后的任何时间,甚至数小时或数天后,损坏都可能出现。
理论上,如果执行对象的线程被另一个线程停止,代码可以这样编写,以防止对象处于无效状态。 也就是说,如果Thread.stop
可以在任何时候被调用,那么很难保证一个有效的状态,甚至尝试这样做也不是很常见,所以这不太可能是作者的意图。 (如果是,代码可能会有注释解释它。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.