【问题标题】:LongAdder: How can the try block fail?LongAdder:try 块怎么会失败?
【发布时间】:2018-11-07 18:39:45
【问题描述】:

我正在详细分析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

【问题讨论】:

  • 这似乎是防御性编码。如果代码以原始开发人员未预料到的方式更改,它可能会失败。
  • Peter 所说的,但表单也暗示了机制:在 casBusy() 中获取了“某种”锁,并且约定总是在 finally 块中释放锁(busy = 0 in这个案例)。遵循约定,即使不是严格要求,也使代码(结构)更易于阅读和理解(至少对我而言)。

标签: java algorithm try-catch finally


【解决方案1】:

这很像在 jdk 代码本身的其他任何地方使用ReentrantLock 的模式。这里的“模式”是你应该总是释放锁,即使发生了异常,所以通常代码写成:

Lock someLock...

try {
    // use someLock
} finally {
    someLock.unlock();
}

由于cellsBusy(它是从busy 重命名的)实际上是一个忙自旋锁,所以这里的模式是一样的。因此:

cellsBusy = 0;

实际上是“释放锁”。所以这并不是真正的失败,因为它是关于释放锁显式。我发现这更容易阅读和推理代码。

【讨论】:

  • +1。我认为使代码“更易于阅读和推理” 很可能是它以这种方式编写的原因。不必仔细检查try 块中的每个操作都不会引发异常,只需将其包装在finally 中意味着您不必阅读try 块即可知道将执行此代码(除了在像停电这样的边缘情况下,无论如何不释放锁是你最不重要的问题)。正确的代码不如明显正确的代码。
【解决方案2】:

此代码 - 以及版本 11 之前的任何其他 Java 代码 - 可能会由于从另一个线程调用已弃用的 Thread.stop 方法而失败。这会导致在目标线程中抛出ThreadDeath 错误,可能在任何时候。但是,does at least stay alive 线程的长度足以让finally 块执行。

Thread.stop 方法已弃用 because this behaviour makes it "inherently unsafe":

为什么不推荐使用 Thread.stop?

因为它本质上是不安全的。停止线程会导致它解锁所有已锁定的监视器。 (当 ThreadDeath 异常向上传播堆栈时,监视器被解锁。)如果以前受这些监视器保护的任何对象处于不一致状态,则其他线程现在可能会查看这些处于不一致状态的对象。据说这些物体已损坏。当线程对损坏的对象进行操作时,可能会导致任意行为。这种行为可能很微妙且难以检测,也可能很明显。与其他未经检查的异常不同,ThreadDeath 以静默方式杀死线程;因此,用户没有警告他的程序可能已损坏。损坏可以在实际损坏发生后的任何时间出现,甚至在未来几小时或几天内。

理论上,代码可以这样编写,以防止在执行对象的线程从另一个线程停止时使对象处于无效状态。也就是说,如果可以随时调用Thread.stop,则很难保证有效状态,甚至尝试这样做也不常见,因此这不太可能是作者的意图。 (如果是,代码可能会有注释说明。)

【讨论】:

  • 我们在这里讨论的是 internal 线程创建,由有才华的人编写...我非常怀疑这与Thread::stop 有什么关系,至少我非常希望。
  • 我完全同意。我只是认为值得从字面上回答 “这个 try 块怎么会失败?”,因为您已经回答了隐含的(并且更有趣的)问题 “为什么要编写这段代码一个尝试块?”.
  • @kaya3:很棒的评论。您使用单词/语言的准确性确实令人印象深刻。我非常喜欢。关于你和尤金的答案,我认为接受尤金的答案是公平的,因为它更接近我想问的问题。
  • @user120513 我同意,尤金的回答更有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-08
  • 2012-08-30
相关资源
最近更新 更多