【问题标题】:Is Thread.interrupt() evil?Thread.interrupt() 是邪恶的吗?
【发布时间】:2011-01-02 12:31:07
【问题描述】:

一位队友提出以下声明:

Thread.interrupt() 本质上是损坏的,应该(几乎)永远不要使用”。

我试图理解为什么会这样。

从不使用Thread.interrupt() 是已知的最佳做法吗?您能否提供证据说明为什么它会损坏/有错误,并且不应该用于编写健壮的多线程代码?

注意 - 我对这个问题不感兴趣,如果它是来自设计防腐剂的“漂亮”。我的问题是 - 它有问题吗?

【问题讨论】:

  • 当他们说坏了时,您确定他们的意思是“越野车”吗?也许他们的意思是“鼓励不良行为/难以维护的代码”,我想说的就是这种情况。
  • 当人们提出这样的声明时,请始终向他们询问为什么它被破坏的技术解释。您的队友可能正在考虑Thread.stop()。也有可能他/她只是在不加思索地重复别人的教条。
  • 很漂亮而且没有马车。您的队友可能将interruptstopsuspend 混淆了。 interrupt 方法在整个java.util.concurrent 包中使用,如果它们不支持,您的任务将无法取消。
  • 再次请解释一下。他提出了其他人不同意的主张,因此请提供参考资料。如果他不能给你一个理由,那么他看起来知道多少都没关系。
  • Thread.interrupt() 有一个美好的人生开端,有爱的家,有爱心的父母,没有人看到邪恶来临...

标签: java multithreading interrupt


【解决方案1】:

短版:

这是一个已知的最佳实践,永远不要 使用 Thread.interrupt()?

没有。

你能提供 证明它为什么坏了/马车, 并且不应该用于写作 健壮的多线程代码?

情况正好相反:它对多线程代码至关重要。

有关示例,请参见 Java Concurrency in Practice 中的清单 7.7。

加长版:

在这里,我们在一个特定的地方使用这种方法:处理InterruptedExceptions。这可能看起来有点奇怪,但这是代码中的样子:

try {
    // Some code that might throw an InterruptedException.  
    // Using sleep as an example
    Thread.sleep(10000);
} catch (InterruptedException ie) {
    System.err.println("Interrupted in our long run.  Stopping.");
    Thread.currentThread().interrupt();
}

这为我们做了两件事:

  1. 避免吃掉中断异常。 IDE 自动异常处理程序始终为您提供 ie.printStackTrace(); 之类的内容和轻松愉快的“TODO:这里需要一些有用的东西!”评论。
  2. 它恢复中断状态,而不在此方法上强制检查异常。如果您正在实施的方法签名没有 throws InterruptedException 子句,这是您传播该中断状态的另一种选择。

一位评论者建议我应该使用未经检查的异常“强制线程终止”。这是假设我事先知道突然终止线程是正确的做法。我不。

在上面引用的列表之前的页面上引用 JCIP 的 Brian Goetz:

一个任务不应该假设任何关于它的中断策略 执行线程,除非它被明确设计为在 具有特定中断策略的服务。

例如,假设我是这样做的:

} catch (InterruptedException ie) {
    System.err.println("Interrupted in our long run.  Stopping.");
    // The following is very rude.
    throw new RuntimeException("I think the thread should die immediately", ie);
}

我要声明的是,不管调用堆栈的其余部分和相关状态的其他义务如何,这个线程现在需要终止。我会试图偷偷溜过所有其他 catch 块和状态清理代码以直接导致线程死亡。更糟糕的是,我会消耗线程的中断状态。上游逻辑现在必须解构我的异常,以试图弄清楚是否存在程序逻辑错误,或者我是否试图将已检查的异常隐藏在一个模糊的包装器中。

例如,团队中的其他人必须立即执行以下操作:

try {
    callBobsCode();
} catch (RuntimeException e) { // Because Bob is a jerk
    if (e.getCause() instanceOf InterruptedException) {
        // Man, what is that guy's problem?
        interruptCleanlyAndPreserveState();
        // Restoring the interrupt status
        Thread.currentThread().interrupt();
    }
}

中断状态比任何特定的InterruptException 更重要。有关具体原因,请参阅Thread.interrupt() 的 javadoc:

如果该线程在调用 wait()、wait(long) 时被阻塞, 或 Object 类或 join() 的 wait(long, int) 方法, join(long)、join(long, int)、sleep(long) 或 sleep(long, int)、方法 这个类,那么它的中断状态将被清除,它会 收到一个 InterruptedException。

如您所见,在处理中断请求时可以创建和处理多个 InterruptedException,但前提是保留该中断状态。

【讨论】:

  • sleep() 只有在有人打断它时才会被打断;在您的处理程序中,您只需将已检查的异常降级为未检查的异常。哎哟。
  • @Will,这就是重点:我接受中断,然后强制中断继续。如果我只是在方法上有一个“throws InterruptedException”子句,那么任何有“catch (Exception e) {}”块的人都会吃掉这个异常。睡眠在 control-C 上被打断:如果你吃到异常,你的线程不会停止。
  • @erickson,谢谢 - 我刚刚添加了列表的链接。我从未意识到他们会将代码示例放在 JCIP 网站上。这应该会派上用场!
  • 我认为您的代码 sn-p 仍然会吃掉异常。如果从其他方法调用代码,然后继续执行长时间运行的操作,则线程将不会完成。如果Thread.currentThread().interrupt(); 基本上位于Runnablerun 方法的结束,则只能按照您的建议进行操作。如果代码可以从任何地方调用,它应该抛出一个未经检查的异常来强制线程死亡。
  • @artbristol 我同意鲍勃的观点;但是,我也支持您的想法,即如果可能,最好也抛出 InterruptedException。个人观点,我认为通过抛出异常来引入bug的可能性较小,并且意图更清晰。但是我认为“调用代码将愉快地继续”的论点并不成立,因为如果调用者本身是一个长时间运行的任务,它应该检查中断状态。否则你是说它不管理自己的取消政策,这是有风险的。
【解决方案2】:

我知道Thread.interrupt() 被破坏的唯一方式是它实际上并没有做它看起来可能的事情——它实际上只能中断监听它的代码.

但是,如果使用得当,它在我看来是一个很好的任务管理和取消的内置机制。

我推荐Java Concurrency in Practice 了解更多关于正确和安全使用它的信息。

【讨论】:

    【解决方案3】:

    Thread.interrupt() 的主要问题是大多数程序员不知道隐藏的陷阱并以错误的方式使用它。例如,当您处理中断时,有些方法会清除标志(因此状态会丢失)。

    此外,调用不会总是立即中断线程。例如,当它在某个系统例程中挂起时,什么也不会发生。事实上,如果线程不检查标志并且从不调用抛出 InterruptException 的 Java 方法,那么中断它不会有任何效果。

    【讨论】:

      【解决方案4】:

      不,这不是越野车。它实际上是在 Java 中停止线程的基础。它在 java.util.concurrent 的 Executor 框架中使用 - 请参阅 java.util.concurrent.FutureTask.Sync.innerCancel 的实现。

      至于失败,我从没见过失败的,我用过很多次。

      【讨论】:

      • 没错。如果您打算在可取消的任务中使用它,那么代码检查中断是非常重要的。 interrupt 没有什么“邪恶”;它仍然受到 Doug Lea 等专家的严重依赖。
      【解决方案5】:

      没有提到的一个原因是中断信号可能会丢失,这使得调用 Thread.interrupt() 方法毫无意义。因此,除非您在 Thread.run() 方法中的代码在 while 循环中旋转,否则调用 Thread.interrupt() 的结果是不确定的。

      【讨论】:

        【解决方案6】:

        我注意到当在线程ONE 中执行DriverManager.getConnection() 时,当没有可用的数据库连接时(假设服务器已关闭,因此最后这一行抛出SQLException)并从线程TWO 我明确地调用@987654326 @,那么 ONE.interrupted()ONE.isInterrupted() 都返回 false,即使放在处理 SQLExceptioncatch{} 块中的第一行。

        当然,我在实现额外信号量时解决了这个问题,但这很麻烦,因为这是我 15 年 Java 开发中的第一个此类问题。

        我想这是因为com.microsoft.sqlserver.jdbc.SQLServerDriver 中的错误。我进行了更多调查,以确认对 native 函数的调用在所有情况下都会消耗此中断,但在成功时会保留它。

        托梅克

        附:我找到了analogous issue

        P.P.S 我附上了我在上面写的一个非常简短的例子。注册的班级可以在sqljdbc42.jar找到。我在 2015 年 8 月 20 日构建的课程中发现了这个错误,然后我更新到可用的最新版本(从 2017 年 1 月 12 日开始),这个错误仍然存​​在。

        import java.sql.*;
        
        public class TEST implements Runnable{
            static{
                try{
        //register the proper driver
                   Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
                }
                catch(ClassNotFoundException e){
                    System.err.println("Cannot load JDBC driver (not found in CLASSPATH).");
                }
            }
        
            public void run() {
                Thread.currentThread().interrupt();
                System.out.println(Thread.currentThread().isInterrupted());
        //prints true
                try{
                    Connection conn = DriverManager.getConnection("jdbc:sqlserver://xxxx\\xxxx;databaseName=xxxx;integratedSecurity=true");
                }
                catch (SQLException e){
                    System.out.println(e.getMessage());
                }
                System.out.println(Thread.currentThread().isInterrupted());
        //prints false
                System.exit(0);
            }
        
            public static void main(String[] args){
                (new Thread(new TEST())).start();
            }
        }
        

        如果您将一些完全不正确的内容(如"foo")传递给DriverManager.getConnection(),您将收到消息“没有为foo 找到合适的驱动程序”,并且第二个打印输出仍然如预期的那样正确。但是,如果您传递了正确构建的字符串,但是,例如,您的服务器已关闭或您丢失了网络连接(这通常发生在生产环境中),您将看到java.net 套接字超时错误打印输出和线程的interrupted()状态为 LOST。

        【讨论】:

          【解决方案7】:

          问题不在于实现没有错误,而是您的线程在被中断时处于未知状态,这可能导致不可预知的行为。

          【讨论】:

          • 中断是优雅的信号,它不会暂停或中止线程。
          • 啊,是的,我的错。停止是邪恶的。谢谢!
          猜你喜欢
          • 1970-01-01
          • 2011-12-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-11-18
          • 2010-11-26
          • 2020-08-10
          • 2010-12-23
          相关资源
          最近更新 更多