【问题标题】:Who is calling the Java Thread interrupt() method if I'm not?如果我不是,谁在调用 Java 线程的 interrupt() 方法?
【发布时间】:2011-01-08 18:41:33
【问题描述】:

我已经阅读并重新阅读 Java 并发实践,我已经阅读了关于这个主题的几个主题,我已经阅读了 IBM 文章 Dealing with InterruptedException,但我认为有些东西我根本没有掌握可以分为两个问题:

  1. 如果我自己从不中断其他线程,什么会触发 InterruptedException

  2. 如果我自己从来没有使用 interrupt() 中断其他线程(比如说因为我正在使用其他方式来取消我的工作线程,例如毒丸和 while (!cancelled) 样式循环 [正如 JCIP 中所解释的]),那么 InterruptedException 是什么意思?抓到一只我该怎么办?关闭我的应用程序?

【问题讨论】:

    标签: java multithreading exception exception-handling


    【解决方案1】:

    如果您决定将您的代码与其他库集成,他们可以在您的代码上调用interrupt()。例如如果您将来决定在ExecutorService 中执行您的代码,那么这可能会通过interrupt() 强制关闭。

    简而言之,我不仅会考虑您的代码在哪里运行现在,还会考虑它将来可能在什么上下文中运行。例如你要把它放在图书馆里吗?一个容器?其他人将如何使用它?你打算重复使用它吗?

    【讨论】:

    • 我以为只有 shutdownNow 调用了 interrupt() 方法。关机也是这样吗?
    【解决方案2】:

    InterruptedException 表示例程可能会被中断,但不一定会被中断。

    如果您不期望中断,那么您应该像对待任何其他意外异常一样对待它。如果它处于一个意外异常可能会产生令人发指的后果的关键部分,最好尝试清理资源并正常关闭(因为获取中断信号表明您正在使用不依赖中断的精心设计的应用程序在某种程度上它不是设计的,所以一定有什么问题)。或者,如果有问题的代码不重要或无关紧要,您可能希望忽略(或记录)中断并继续进行。

    【讨论】:

      【解决方案3】:

      线程中断机制是让(合作的)线程响应请求以停止其正在执行的操作的首选方式。任何线程(包括我认为的线程本身)都可以在线程上调用interrupt()

      在实践中,interrupt() 的正常用例涉及某种框架或管理器告诉某个工作线程停止他们正在做的事情。如果工作线程是“中断感知”的,它将注意到它已通过异常或定期检查其中断标志而被中断。当注意到它被中断时,一个表现良好的线程会放弃它正在做的事情并结束自己。

      假设上述用例,如果您的代码在 Java 框架内或从某个工作线程中运行,它可能会被中断。当它被中断时,你的代码应该放弃它正在做的事情并以最合适的方式结束它。根据调用代码的方式,这可以通过返回或抛出一些适当的异常来完成。但它可能不应该调用System.exit()。 (你的应用不一定知道为什么会被中断,当然也不知道是否还有其他线程需要被框架中断。)

      另一方面,如果您的代码并非设计为在某些框架的控制下运行,您可能会认为InterruptedException 是一个意外异常;即一个错误。在这种情况下,您应该像对待其他错误一样对待异常;例如将其包装在未经检查的异常中,并在处理其他意外未经检查的异常的同时捕获并记录它。 (或者,您的应用程序可以简单地忽略中断并继续执行它正在执行的操作。)


      1) 如果我自己从不中断其他线程,什么会触发 InterruptedException?

      一个例子是,如果您的Runnable 对象使用ExecutorService 执行,并且在服务上调用shutdownNow()。理论上,任何第 3 方线程池或线程管理框架都可以合法地做这样的事情。

      2) 如果我自己从来没有使用 interrupt() 来中断其他线程……那么InterruptedException 是什么意思?抓到一只我该怎么办?关闭我的应用?

      您需要分析代码库以找出进行interrupt() 调用的原因以及原因。一旦你弄清楚了这一点,你就可以计算出>>你的

      在您知道为什么会抛出 InterruptedException 之前,我建议您将其视为硬错误;例如将堆栈跟踪打印到日志文件并关闭应用程序。 (显然,这并不总是正确的答案......但关键是这是一个“错误”,需要引起开发人员/维护人员的注意。)

      3) 我如何知道是谁/什么在打电话interrupt()

      对此没有好的答案。我能建议的最好方法是在Thread.interrupt() 上设置一个断点并查看调用堆栈。

      【讨论】:

        【解决方案4】:

        问题的问题是“我”。 “I”通常指一个类的单个实例。我的意思是,任何特定的低级代码(类)都不应该依赖于整个系统的实现。话虽如此,您确实已经做出了一些“架构”决定(例如在什么平台上运行)。

        来自 JRE 的可能的意外中断是 java.util.concurrent 中的取消任务和关闭小程序。

        线程中断的处理通常写得不正确。因此,我建议架构决策尽可能避免造成中断。但是,代码处理中断应始终正确编写。现在无法将中断带出平台。

        【讨论】:

        • 嗨,汤姆,我记得你的名字来自 c.l.j.p。 ;) 好吧,确切地说:我从来不需要自己抛出 interrupt() ...除非我正在捕获 InterruptedException 并且需要重新断言中断状态,但这仍然不是 100% 清楚我。我是新来的,对赞成票和答案/cmets(正确的和错误的)的数量感到惊讶:显然这是一个不简单的主题,或者至少通常没有很好地解释。多亏了所有的帖子,我开始更清楚地了解正在发生的事情:)
        【解决方案5】:

        如前所述,另一个库可以中断您的线程。即使库没有从您的代码中显式访问线程,它们仍然可以获取正在运行的线程列表并使用以下method 以这种方式中断它们。

        【讨论】:

          【解决方案6】:

          正如其他人指出的那样,中断线程(实际上是中断阻塞调用)通常用于干净地退出或取消正在进行的活动。

          但是,您不应将InterruptedException 单独视为“退出命令”。相反,您应该将中断视为控制线程运行状态的一种方式,这与Object.notify() 所做的方式非常相似。就像您从对 Object.wait() 的调用中醒来后检查当前状态一样(您不认为唤醒意味着您的等待条件已得到满足),在被中断轻推后,您应该检查 为什么你被打断了。通常有一种方法可以做到这一点。例如,java.util.concurrent.FutureTask 有一个 isCancelled() 方法。

          代码示例:

          public void run() {
              ....
              try {
                  .... // Calls that may block.
              } catch (InterruptedException e) {
                  if (!running) {  // Add preferred synchronization here.
                      return; // Explicit flag says we should stop running.
                  }
                  // We were interrupted, but the flag says we're still running.
                  // It would be wrong to always exit here. The interrupt 'nudge'
                  // could mean something completely different. For example, it
                  // could be that the thread was blocking on a read from a particular
                  // file, and now we should read from a different file.
                  // Interrupt != quit (not necessarily).
              }
              ....
          }
          public void stop() {
              running = false; // Add preferred synchronization here.
              myThread.interrupt();
          }
          

          【讨论】:

            【解决方案7】:

            您可以通过创建自己的线程类(扩展java.lang.Thread)并覆盖interrupt() 方法来学习这一点,在该方法中,您将堆栈跟踪记录到一个字符串字段中,然后转移到super.interrupt()。

            public class MyThread extends Thread {
            
                public volatile String interruptStacktrace; // Temporary field for debugging purpose.
            
                @Override
                public void interrupt() {
                    interruptStacktrace = dumpStack(); // You implement it somehow...
            
                    super.interrupt();
                }
            }
            

            【讨论】:

              【解决方案8】:

              我想我明白你为什么对中断有点困惑。请考虑我的回答:

              如果我自己从不中断其他线程,什么会触发 InterruptedException

              首先你可以中断其他线程;我知道在 JCiP 中提到你不应该中断你不拥有的线程。但是,必须正确理解此声明。这意味着您可能在任意线程中运行的代码不应处理中断,因为由于它不是线程的所有者,因此它不知道其中断策略。所以你可以在其他线程上请求中断,但让其所有者采取中断动作的过程;它封装了中断策略,而不是您的任务代码;至少要礼貌地设置中断标志!

              仍然有很多中断的原因,可能是超时、JVM 中断等。

              如果我自己从未使用 interrupt() 中断其他线程(比如因为我正在使用其他方式取消我的工作线程,例如毒丸和 while (!cancelled) 样式循环 [正如 JCIP 中所解释的) ),那么 InterruptedException 是什么意思呢?抓到一只我该怎么办?关闭我的应用?

              这里你需要非常小心;如果您拥有抛出 InterruptedException (IE) 的线程,那么您知道捕获它后该怎么做,说您可以关闭您的应用程序/服务,或者您可以用一个新线程替换这个被杀死的线程!但是,如果您不拥有该线程,则在捕获 IE 时,要么将其重新抛出调用堆栈的更高位置,要么在执行某些操作(可能正在记录)之后,重置中断状态,以便拥有该线程的代码在控制到达它时,可能知道线程被中断了,因此采取行动,因为只有它知道中断策略。

              希望这会有所帮助。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2014-12-13
                • 1970-01-01
                • 2016-10-27
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多