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

时间:2012-06-03 06:01:47

标签: java multithreading exception-handling blocking

如果方法必须是阻止方法,我是否正确地想,如果我离开 出throws InterruptedException,我犯了一个错误?

简而言之:

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

这是对的吗?

3 个答案:

答案 0 :(得分:23)

不,我发现您的摘要不正确。 通常是,如果你正在编写一个方法来调用抛出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;
}

或者,如果您只想尊重中断请求,则可以在不涉及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;
}

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

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

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

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

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

我建议您研究线程中断状态的主题,并熟悉方法Thread#interrupted()Thread#interrupt()和{{3}}。一旦你理解了这些内容,并且发现InterruptedException正在飞行中Thread#isInterrupted()的替代代表已经返回true,或者Thread#interrupted()的礼貌翻译已经返回true,那么这一切都应该开始制作更有意义。

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

答案 1 :(得分:3)

当方法阻塞的线程被InterruptedException调用时,(通常)抛出

interrupt()

关键是取消阻止(由于某种原因)被阻止的线程。原因的例子是应用程序关闭。因此,当您关闭应用程序时,如果您有线程等待,请说sleep()wait(),如果您没有告诉他们您正在关闭,他们将继续wait()。如果这些线程是不是守护程序线程,那么您的应用程序将不会关闭。

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

由于某些其他原因,线程可能会被中断,但重点是相同的。 如果你有多线程应用程序,你必须为你的线程建立协议,以便他们知道何时有一些特殊条件如何处理它。如果线程正在等待/休眠,您必须将其唤醒以处理该情况。 您的库或框架的clinets不知道有关您的协议的任何内容,因此他们不知道如何处理InterruptedException,因为recomendation将在您的库/框架代码中处理它。

答案 2 :(得分:0)

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

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

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

编辑:

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

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

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

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