【问题标题】:Interrupting a thread 'gracefully' while inside the main loop在主循环内“优雅地”中断线程
【发布时间】:2015-05-22 10:20:56
【问题描述】:

假设我有以下代码:

public void run(){
   while (true){
        function1();
        ...
        functionN();
   }
}

我想“优雅地”退出 - 这对我来说意味着一旦我发送了一个关闭信号并且当前线程处于 functionK(),该线程将“打破”循环并退出运行。

所以我尝试像这样使用 Thread.interrupt():

public void run(){
   while (true){
        try {
            function1();
            ...
            functionN();
        } catch (InterruptedException ex) {
              /* Cleanup and exit. */
        }
   }
}

但这不起作用 - 即使打开中断标志,线程也会继续无休止地运行。

仅作记录:

public void run(){
   while (!thread.isInterrupted()){
        try {
            function1();
            ...
            functionN();
        } catch (InterruptedException ex) {
              /* Cleanup and exit. */
        }
   }
}

停止循环,但对我没有帮助。由于每个函数执行的操作可能需要几分钟,并且有很多不同的函数,因此在每个函数之前检查中断标志是否为一个可能会很昂贵(特别是因为大多数情况下应用程序运行顺利)。

我想知道是否有一种特殊的机制可以用来解决这类问题。

【问题讨论】:

  • 如果有多个线程,那么它是可能的!使用同步。
  • 这段代码只有一个线程工作。

标签: java multithreading concurrency


【解决方案1】:

API documentation 对此非常清楚:

如果此线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法或 join()、join(long)、join(long , int), sleep(long) 或 sleep(long, int) 方法,则其中断状态将被清除并收到 InterruptedException。

如果此线程在 InterruptibleChannel 上的 I/O 操作中被阻塞,则通道将关闭,线程的中断状态将被设置,线程将收到 ClosedByInterruptException。

如果此线程在 Selector 中被阻塞,则线程的中断状态将被设置,并从选择操作中立即返回,可能返回非零值,就像调用了选择器的唤醒方法一样。

如果前面的条件都不成立,则设置该线程的中断状态。

因此,如果您正在等待对象监视器,则只能依赖此异常。某些 I/O 操作还会引发其他一些异常,但如果您也不使用它们,那么除了检查 interrupted() 标志之外别无选择。

你可以做的是重新组织你的代码:如果你有N方法被一个接一个地调用,是不是不可能将它们抽象成一个循环?通常可以找到一种方法来重构代码以支持中断,具体方法取决于您的实际情况。我的第一个问题是:为什么一个方法会运行几分钟?这听起来有点可疑(尽管这可能是有道理的)。

无论哪种方式,可中断性都不是免费的,如果您希望代码对中断的响应比主循环的长度更快,则必须主动设计中断点。

还有一件事:检查interrupted() 标志绝对是昂贵的。不是当你在主循环中花费几分钟的时候,它比构造和处理异常要便宜得多。我什至可以说,您会发现很少有比致电Thread.isInterrupted() 更快的事情了。

【讨论】:

    【解决方案2】:

    实际上,如果您在方法中执行 CPU 密集型工作,您必须自己检查 Thread.interrupted() 并自己抛出 InterruptedException。 Java 不会神奇地为您做到这一点,除非您将车停在一些专门设计的空间,例如 Semaphore.wait() 等。

    【讨论】:

      【解决方案3】:

      您的第二个示例将在中断后继续循环。这是因为InterruptedException 实际上并不意味着设置了线程的中断标志;事实上,你根本不需要检查它。

      要解决这个问题,您可以简单地重新中断线程(让调用者知道线程已被中断),然后中断:

      public void run(){                                                                                  
        while (true) {                                                                                    
          try {                                                                                           
            function1();                                                                                  
            //...                                                                                  
            functionN();                                                                                  
          } catch (InterruptedException ex) {                                                             
            Thread.currentThread().interrupt();                                                           
            break;                                                                                        
          }                                                                                               
        }                                                                                                 
      }  
      

      【讨论】:

      • 我认为这些函数不会抛出 InterruptedException。
      • 确实不是。所以我什至没有遇到中断的异常。
      • 好吧,至少其中一个可以,否则代码将无法编译。
      • @AndyTurner 不是真的,只有方法必须具有声明它可能抛出 InterruptedException 的签名。我认为问题在于这些都不是。
      【解决方案4】:

      正如“Java 并发实践”一书中所述:“Java 没有提供任何机制来安全地强制线程停止正在执行的操作”,因此您必须在自己身边实现一些东西。检查中断标志并处理 InterruptedException 是管理线程取消的最佳方法。 如果你们中的一个 function1()...functionN() 处于数据库事务的中间,或者 HTTP 调用取决于您的程序来处理取消;您可以等到 n 秒并保存当前状态或取消事务和回滚,要执行的操作由您的应用程序逻辑决定。

      【讨论】:

        猜你喜欢
        • 2019-11-10
        • 1970-01-01
        • 2013-03-24
        • 2011-02-14
        • 1970-01-01
        • 2016-09-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多