【问题标题】:Why is swallowing InterruptedException ok for subclasses of Thread?为什么对于 Thread 的子类可以吞下 InterruptedException?
【发布时间】:2010-10-27 12:24:03
【问题描述】:

在 Brian Goetz 的 article on how to handle InterruptedException 中,有一段很突出:

当您知道线程即将退出时,可以接受中断。仅当调用可中断方法的类是 Thread 的一部分而不是 Runnable 时,才会发生这种情况。

我不明白。与 Runnable 有关的原因是否可能由线程池处理,而 Thread 是您自己开始的?

【问题讨论】:

    标签: java multithreading exception-handling concurrency


    【解决方案1】:

    基本上。文章中表达的担忧是,如果您吞下中断异常,那么调用堆栈中更高的代码将不知道中断,可能会导致不良行为。如果你启动线程,那么你就知道调用堆栈中没有更高级别的东西关心被中断,这个线程不会继续生活在线程池中,所以就让线程死掉。

    我讨厌 InterruptedException,我认为它给检查的异常起了一个坏名声,而本文并没有改变这种观点。如果此异常非常重要,以至于通过调用堆栈传递,则 Runnable.run() 应该在方法声明中声明它,以便您可以简单地重新抛出它,或者它应该是一个未经检查的异常,出于同样的原因 SecurityException 是未经检查的例外。

    如果您想知道,如果方法被中断,我更喜欢的设计是返回一个布尔值,但本文确实说明了这不一定是实际的。

    【讨论】:

    • 实现 Callable 可能会帮助您返回布尔值。 Callable 还允许您向上抛出异常。
    • 返回布尔值是个坏主意。如果它没有清除状态,则在典型的等待循环中错过返回值并旋转。如果它确实清除了状态,那么我们已经中断了线程。检查的异常清楚地表明了这一点(也许,鉴于我见过的代码,不是每个人)。未经检查的异常可能会以合理的方式导致退出。
    • @Yishai,我并不是很讨厌 InterruptedException。从多线程的角度来看,很高兴从方法签名中知道是可中断的。
    • @Tom Hawtin,考虑到文章中的担忧,我同意将其设为布尔值不一定是个好主意。这对我来说绝对是有益的。话虽如此,这里有很多权衡,实践经验表明检查异常路由没有成功,主要是因为重新抛出不是适当的响应 - 这令人惊讶,因此被吞咽处理不当。
    【解决方案2】:

    我认为扩展Thread 是不必要的,因此最好实现Runnable

    但重要的是代码知道线程将要退出。如果您的代码是一些通用回调接口的一部分,那么您将无法知道您是如何被使用的。您可能会被传递到一个线程池(实际上,我们可能应该使用池而不是在代码中不适当的位置构造Threads)。 OTOH,通常Runnable 是一个匿名内部类,因此,在源代码级别,它是确实知道发生了什么的封闭方法的一部分。

    所以,如果线程即将退出,那么重置当前线程的中断状态是没有意义的,因为没有什么可以中断的。

    在某些时候,您会想说它已经中断了足够多的时间。例如,线程池即使在任务被中断后也可以继续使用线程,尽管它们可能希望为尝试获取任务的调用者保留InterruptException

    库通常不能正确处理中断。 IMO,中断没有有意义的上下文。没有他们,生活会简单得多,不幸的是,他们让人们感受到了他们的存在。

    【讨论】:

      【解决方案3】:

      我同意其他人的观点,区别在于您是否控制该线程。如果您扩展了一个线程,那么您几乎可以控制该线程。另一方面,如果您的代码只是一个 Runnable,它可能会在您不拥有的借用线程(例如来自线程池)上运行。通过吃掉异常并且不恢复中断状态,您剥夺了上级代码识别中断并采取行动的机会。

      我认为,InterruptedException 作为一个检查异常是一件好事。 InterruptedException 是一种请求取消任务的方法。假设有人以 Runnable 的形式编写了一个任务,该任务涉及一个引发 InterruptedException 的阻塞方法。如果它不是已检查异常,如果您不小心,您可能不会考虑对 InterruptedException 采取行动(因此取消)并您自己的清理

      public class MyTask implements Runnable {
          public void run() {
              while (someCondition) {
                  Object value = someBlockingQueue.take();
                  // act on the value and loop back
              }
          }
      }
      

      由于 InterruptedException 是一个检查异常,我的任务应该如何响应中断(取消)是最重要的。

      public class MyTask implements Runnable {
          public void run() {
              while (someCondition) {
                  try {
                      Object value = someBlockingQueue.take();
                      // act on the value and loop back
                  } catch (InterruptedException e) {
                      // I'm being cancelled; abort
                      cleanUp();
                      // restore the interrupt
                      Thread.currentThread().interrupt();
                      break;
                  }
              }
          }
      }
      

      【讨论】:

      • 我认为,无论是否存在异常,您都可能希望执行 cleanUp() - 它应该存在于 finally{} 块中。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多