35

一位队友提出以下主张:

Thread.interrupt()天生就坏了,应该(几乎)永远不要使用”。

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

这是一个从不使用的已知最佳实践Thread.interrupt()吗?你能提供证据为什么它被破坏/错误,并且不应该用于编写健壮的多线程代码吗?

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

4

7 回答 7

47

简洁版本:

从不使用 Thread.interrupt() 是已知的最佳实践吗?

不。

你能提供证据为什么它被破坏/错误,不应该用于编写健壮的多线程代码吗?

反之亦然:它对多线程代码至关重要。

有关示例,请参见Java 并发实践中的清单 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 :

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

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

于 2010-01-07T15:02:12.817 回答
16

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

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

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

于 2010-01-07T14:45:21.927 回答
12

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

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

于 2010-01-07T14:43:32.320 回答
4

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

至于失败,我从来没有见过失败,而且我已经广泛使用它。

于 2010-01-07T15:10:37.557 回答
1

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

于 2012-10-11T10:33:46.073 回答
1

我注意到,当ONE我在线程中执行DriverManager.getConnection()时,没有可用的数据库连接(说服务器关闭,因此最后这条线抛出SQLException)并且从TWO我明确调用的线程ONE.interrupt()中,然后两者都ONE.interrupted()返回ONE.isInterrupted()false即使放置在catch{}块中的第一行SQLException是处理。

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

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

托梅克

PS我发现了类似的问题

PPS 我附上了我在上面写的一个非常简短的例子。注册的类可以在 中找到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。

于 2017-05-16T19:30:55.453 回答
-3

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

于 2010-01-07T14:45:21.117 回答