一位队友提出以下主张:
“
Thread.interrupt()
天生就坏了,应该(几乎)永远不要使用”。
我试图理解为什么会这样。
这是一个从不使用的已知最佳实践Thread.interrupt()
吗?你能提供证据为什么它被破坏/错误,并且不应该用于编写健壮的多线程代码吗?
注意- 如果设计防腐剂“漂亮”,我对这个问题不感兴趣。我的问题是 - 它有问题吗?
一位队友提出以下主张:
“
Thread.interrupt()
天生就坏了,应该(几乎)永远不要使用”。
我试图理解为什么会这样。
这是一个从不使用的已知最佳实践Thread.interrupt()
吗?你能提供证据为什么它被破坏/错误,并且不应该用于编写健壮的多线程代码吗?
注意- 如果设计防腐剂“漂亮”,我对这个问题不感兴趣。我的问题是 - 它有问题吗?
简洁版本:
从不使用 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();
}
这为我们做了两件事:
ie.printStackTrace();
“TODO:一些有用的东西需要去这里!”之类的东西。评论。 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,但前提是保留该中断状态。
我知道的唯一方法Thread.interrupt()
是它实际上并没有做它看起来可能的事情——它实际上只能中断监听它的代码。
但是,如果使用得当,在我看来,它是一个很好的任务管理和取消的内置机制。
我推荐Java Concurrency in Practice以了解更多关于正确和安全使用它的信息。
主要问题Thread.interrupt()
是大多数程序员不知道隐藏的陷阱并以错误的方式使用它。例如,当您处理中断时,有一些方法可以清除标志(因此状态会丢失)。
此外,调用不会总是立即中断线程。例如,当它在某个系统例程中挂起时,什么也不会发生。事实上,如果线程不检查标志并且从不调用抛出的Java方法InterruptException
,那么中断它不会有任何效果。
不,这不是越野车。它实际上是在 Java 中停止线程的基础。它在 java.util.concurrent 的 Executor 框架中使用 - 请参阅java.util.concurrent.FutureTask.Sync.innerCancel
.
至于失败,我从来没有见过失败,而且我已经广泛使用它。
未提及的一个原因是中断信号可能会丢失,这使得调用 Thread.interrupt() 方法毫无意义。因此,除非您在 Thread.run() 方法中的代码在 while 循环中旋转,否则调用 Thread.interrupt() 的结果是不确定的。
我注意到,当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。
问题不在于实现没有错误,而是您的线程在被中断时处于未知状态,这可能导致不可预测的行为。