13

如果一个方法必须是一个阻塞方法,我是否认为如果我遗漏了throws InterruptedException,我就犯了一个错误?

简而言之:

  • 一个阻塞方法应该包括throws InterruptedException否则是一个正常的方法。
  • 阻塞方法可能会影响响应性,因为很难预测它何时会完成,这就是它需要的原因throws InterruptedException

那是对的吗?

4

3 回答 3

27

不,我不认为你的总结是正确的。通常,如果您正在编写一个调用其他 throwInterruptedException的方法,那么您的方法也应该宣传 throwing InterruptedException— 除非您对当您的方法所依赖的方法发出中断信号时该怎么做有一个很好的计划。

您能够吸收这种干扰的情况很少见。也许您正在计算一个迭代解决方案,其中精度随着时间的推移而增加,但是,在您的调用线程被中断时,您认为您在分配的时间内达到的解决方案已经足够好,并且仍然足够正确以返回。换句话说,该解决方案仍在您的方法范围内。

想象:

private double improveUpon(double start) throws InterruptedException {
  // ...
}


public double compute() {
  double result = 0.0;
  try {
    do {
      result = improveUpon(result);
    } while (couldBeImproved(result));
  } catch (InterruptedException ex) {
    Thread.currentThread().interrupt();
  }
  return result;
}

或者,如果您只想尊重中断请求,您可以在不InterruptedException参与的情况下这样做:

private double improveUpon(double start) {
  // ...
}


public double compute() {
  final Thread current = Thread.currentThread();
  double result = 0.0;
  do {
    result = improveUpon(result);
  } while (couldBeImproved(result) &&
           !current.isInterrupted());
  return result;
}

对于另一种变体,请考虑您的方法必须完成其所有工作或向调用者指示它无法完成它的情况,并且需要一段时间才能到达那里,但您希望尊重线程中断。这样的事情就足够了:

private double improveUpon(double start) {
  // ...
}


public double compute() throws InterruptedException {
  final Thread current = Thread.currentThread();
  double result = 0.0;
  do {
    if (current.interrupted())
      throw new InterruptedException();
    result = improveUpon(result);
  } while (!isAdequate(result));
  return result;
}

请注意,我们调用了 on ,如果已设置Thread#interrupted(),它的副作用是清除线程的中断状态。如果该方法返回 true,我们作为调用者已经接受了保持和传达中断状态的责任。在这种情况下,由于我们没有假设我们创建了调用线程,并且我们在这里没有足够的可见范围来了解它的中断策略是什么,所以我们通过 throwing 传达了我们观察和采用的中断状态InterruptedException

将方法标记为“阻塞”始终是程度问题。每个方法都会阻塞它的调用者一段时间。您可能正在寻找的区别是该方法是否阻止等待某些外部输入,例如用户按键或通过网络到达的消息。在这些情况下,您抛出的广告InterruptedException向您的调用者表明,您的方法对于来自必须控制其延迟的线程的调用者是安全的。你是说,“这可能需要一段时间才能完成,但不会比你愿意等待的时间长。” 你在说,“我会一直跑,直到你告诉我不要。” 这不同于,比如说,它威胁要阻塞,直到三个条件之一发生,其中没有一个java.io.InputStream#read()是调用者的线程被中断。

在大多数情况下,您的决定归结为回答以下问题:

  • 为了满足我的方法的要求,我是否需要调用任何抛出的方法InterruptedException
  • 如果是这样,我到那时为止所做的工作对我的来电者有用吗?
  • 如果没有,我也应该扔InterruptedException
  • 如果我没有调用任何东西InterruptedException,我应该尊重我的调用线程的中断状态吗?
  • 如果是这样,在我检测到我的呼叫者的任何用途被打断之前,我是否已经完成了任何工作?
  • 如果没有,我应该扔InterruptedException.

检测到当前线程的中断并将其吞下的情况通常仅限于您(作者)创建了有问题的线程,并且您已承诺run()一旦线程中断就退出线程的方法。这就是“合作取消”的概念,在该概念中,您观察线程停止运行的请求,然后您决定通过尽快完成工作并让线程的调用堆栈展开来遵守该请求。同样,除非您是线程run()方法的作者,否则吞下线程的中断状态可能会损害调用者和他们调用的其他方法的预期行为。

我建议你研究线程中断状态的主题,熟悉方法Thread#isInterrupted()、、Thread#interrupted()Thread#interrupt()。一旦你理解了这些,并且看到InterruptedException飞行中的存在是回归真实的另一种表示Thread#isInterrupted(),或者回归真实的礼貌翻译Thread#interrupted(),这一切都应该开始变得更有意义。

如果您需要更多示例来学习,请说出来,我可以在这里添加建议。

于 2012-06-03T13:17:45.043 回答
4

InterruptedException(通常)在方法上阻塞的线程被interrupt()调用时抛出。

它的重点是解除阻塞(出于某种原因)被阻塞的线程。原因的示例是应用程序关闭。所以,当你关闭你的应用程序时,如果你有线程在等待,sleep()或者说wait(),如果你没有告诉他们你正在关闭,他们将继续wait()。如果这些线程不是守护线程,那么您的应用程序将不会关闭。

因此,当线程在 期间被中断时sleep(),您必须检查条件并处理这种情况。在关闭的情况下,您必须检查您的shutdown标志并最终进行清理工作并让线程离开。

线程可能由于其他一些原因而被中断,但要点是一样的。如果您有多线程应用程序,您必须为您的线程建立协议,以便他们知道何时有一些特殊情况如何处理它。如果线程正在等待/休眠,您必须将其唤醒以处理这种情况。您的库或框架的客户对您的协议一无所知,因此他们不知道如何处理InterruptedException,因为建议在您的库/框架代码中处理它。

于 2012-06-03T06:43:24.490 回答
-2

如果您的方法阻塞,它应该捕获并处理InterruptedException,并且不希望重新抛出它。

此外,该方法可能会在多个地方阻塞 - 每个地方都应该InterruptedException以适合可能抛出它的地方的方式捕获和处理。

关于多线程代码主题的圣经是Java Concurrency in Practice。我强烈推荐你阅读它。

编辑:

在设计并发代码时,请意识到:

  • 根据 JVM 规范,JVMInterruptedException可能会无缘无故地随机抛出(称为“虚假唤醒”)
  • 许多线程可能在相同的条件下等待,所有线程都可能被唤醒(例如通过notifyAll()),但只有一个线程在被中断时可以前进

所以每当一个线程被唤醒时,它应该检查它正在等待的等待条件的状态,并可能回到等待状态。

因此,正确编写的并发代码应该可以捕获InterruptedException. 您可以选择重新抛出它或抛出您自己的应用程序特定异常。“应用程序代码”方法应该更喜欢抛出“应用程序”异常,但是如果您的等待代码可能会发现自己处于无法找出“出了什么问题”的状态,那么您唯一的选择就是抛出InterruptedException.

于 2012-06-03T06:19:39.610 回答