什么时候应该抛出InterruptedException,我应该如何处理呢?(阻塞方法)

962*_*001 11 java multithreading exception-handling blocking

如果一种方法必须是阻止方法,我是否正确地认为如果我遗漏throws InterruptedException,我犯了一个错误?

简而言之:

  • 阻塞方法应该包括throws InterruptedException另外一种常规方法.
  • 阻塞方法可能会影响响应能力,因为很难预测它何时会完成,这就是它需要的原因throws InterruptedException.

那是对的吗?

seh*_*seh 24

不,我发现您的摘要不正确.通常情况下,如果你正在编写一个调用其他投掷InterruptedException方法的方法,那么你的方法也应该广告投掷 - 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;
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您只想尊重中断请求,则可以在不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;
}
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,我们调用了它Thread#interrupted(),如果已经设置,则会产生清除线程中断状态的副作用.如果该方法返回true,则我们作为调用者已承担保留和传达该中断状态的责任.在这种情况下,由于我们不假设我们创建了调用线程,并且我们在这里没有足够的范围可以知道它的中断策略是什么,我们传达了我们通过throw观察和采用的中断状态InterruptedException.

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

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

  • 为了满足我的方法的要求,我是否需要调用任何抛出的方法InterruptedException
  • 如果是这样,我的工作是否已经完成了对我的来电者的任何使用?
  • 如果没有,我也应该抛出InterruptedException.
  • 如果没有我称之为抛出InterruptedException,我应该尊重我的呼叫线程的中断状态吗?
  • 如果是这样的话,我做过的任何工作都达到了这样的程度,即我发现我被调用者的任何使用中断了吗?
  • 如果没有,我应该抛出InterruptedException.

一个人将检测当前线程的中断并吞下它的情况通常局限于那些你,作者,创建有问题的线程,并且你已经承诺run()在线程被中断后退出线程的方法.这就是"合作取消"的概念,其中您观察到线程停止运行的请求,并且您决定通过尽快完成工作并让线程的调用堆栈放松来遵守该请求.但是,除非您是线程run()方法的作者,否则您吞下线程的中断状态可能会损害您的调用者以及他们调用的其他方法的预期行为.

我建议你研究的线程的话题中断状态,并获得舒适的方法Thread#isInterrupted(),Thread#interrupted()Thread#interrupt().一旦你理解了那些,并且看到一个InterruptedException飞行中的人是一个Thread#isInterrupted()已经返回真实的替代表示,或者一个有礼貌的翻译Thread#interrupted()已经返回真实,这应该都开始变得更有意义了.

如果您需要更多示例来学习,请说明,我可以在此处添加建议.

  • 我非常理解虚假唤醒,它们与本次讨论没有密切关系。在我的示例中,您在哪里看到任何容易受到虚假唤醒的调用? (2认同)
  • @Bohemian:虚假唤醒与 InterruptedExceptions 无关。当 `wait()` 在没有任何 `notify` 调用的情况下返回时,会发生虚假唤醒。InterruptedException 当被可中断阻塞调用阻塞的线程被中断时发生。 (2认同)
  • 很棒的 +1。你要问自己的最终问题清单是完美的。 (2认同)

Op *_*kel 5

InterruptedException当调用方法上的线程阻塞时(通常)抛出该异常interrupt()

其目的是解除(由于某种原因)被阻塞的线程的阻塞。原因的示例是应用程序关闭。因此,当您关闭应用程序时,如果有线程在等待,比如说sleep()wait(),如果您不告诉它们您正在关闭,它们将继续关闭wait()。如果这些线程不是守护线程,那么您的应用程序将不会关闭。

因此,当线程在 期间被中断时sleep(),您必须检查条件并处理情况。如果关闭,您必须检查shutdown标志并最终完成清理工作并释放线程。

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


Boh*_*ian -1

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

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

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

编辑:

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

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

因此,每当线程被唤醒时,它都应该检查它正在等待的等待条件的状态,并可能返回等待状态。

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

  • JDK 中几乎所有的阻塞方法都会抛出 InterruptedException。而且您将虚假唤醒与“虚假异常”(据我所知,不存在)混淆了。圣经所说的恰恰是,当 InterruptedException 发生并且您不知道如何处理时,最好的办法就是传播它。重新阅读 JCIP 第 7.1.3 节。 (5认同)
  • 我仍然认为你的答案是完全错误的。InterruptedException 是抛出的最佳异常,以表明由于线程被中断而无法完成工作。并且不存在虚假中断。InterruptedException 可能**不会** 由 JVM 随机抛出。 (4认同)