在主循环内部'优雅地'中断线程

时间:2015-03-19 10:35:46

标签: java multithreading concurrency

说我有以下代码:

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. */
        }
   }
}

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

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

4 个答案:

答案 0 :(得分:3)

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()标志肯定是 NOT 代价高昂。当你在主循环中花费几分钟时,它并不比构造和处理异常便宜得多。我甚至可以说,与Thread.isInterrupted()的通话相比,你发现的事情要快得多。

答案 1 :(得分:3)

实际上,如果你在方法中进行CPU绑定工作,你必须自己检查Thread.interrupted()并自己抛出InterruptedException。除非你停在一些专门设计的空间,例如Semaphore.wait()等,否则Java不会神奇地为你做这件事。

答案 2 :(得分:2)

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

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

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

答案 3 :(得分:2)

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