队友提出以下要求:
“
Thread.interrupt()
本质上是破碎的,应该(几乎)永远不会被使用”。
我试图理解为什么会这样。
从来没有使用Thread.interrupt()
这是一个众所周知的最佳做法吗?你能提供证据证明它为何被破坏/出错,并且不应该用于编写强大的多线程代码吗?
注意 - 如果从设计防腐剂中“非常”,我对这个问题不感兴趣。我的问题是 - 它有缺陷吗?
答案 0 :(得分:40)
简短版:
这是一个从未有过的最佳实践 使用Thread.interrupt()?
没有
你能提供吗? 证据为什么它被打破/ buggie, 不应该用于写作 强大的多线程代码?
反之亦然:它对于多线程代码至关重要。
参见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();
}
这对我们来说有两件事:
ie.printStackTrace();
和轻松的“TODO:有用的东西需要去这里!”评论。 throws InterruptedException
子句,则这是传播该中断状态的另一种选择。一位意见提供者建议我应该使用未经检查的异常“强制线程死亡”。这假设我事先知道突然杀死线程是正确的事情。我不。
在上面引用的列表之前的页面上引用JCIP的Brian Goetz:
任务不应该假设其中断政策 执行线程,除非明确设计为在a中运行 具有特定中断策略的服务。
例如,假设我这样做了:
} 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()时阻塞了这个线程,请等待(long), 或者等待(long,int)Object类或join()的方法, join(long),join(long,int),sleep(long)或sleep(long,int)方法 这个类,然后它的中断状态将被清除 收到InterruptedException。
正如您所看到的,可以在处理中断请求时创建和处理多个InterruptedException,但前提是保留该中断状态。
答案 1 :(得分:16)
我知道Thread.interrupt()
被破坏的唯一方法是,它实际上并没有像它看起来那样 - 它实际上只能中断侦听的代码为了它。
然而,正确使用,在我看来,它似乎是一个很好的内置机制,用于任务管理和取消。
我建议Java Concurrency in Practice阅读有关正确和安全使用它的更多信息。
答案 2 :(得分:11)
Thread.interrupt()
的主要问题是大多数程序员不知道隐藏的陷阱并以错误的方式使用它。例如,当您处理中断时,有一些清除标志的方法(因此状态会丢失)。
此外,调用并不总是立即中断线程。例如,当它在一些系统例程中挂起时,什么都不会发生。实际上,如果线程没有检查标志并且从不调用抛出InterruptException
的Java方法,那么中断它将不会产生任何影响。
答案 3 :(得分:3)
不,这不是马车。它实际上是你如何在Java中停止线程的基础。它在java.util.concurrent的Executor框架中使用 - 请参阅java.util.concurrent.FutureTask.Sync.innerCancel
的实现。
至于失败,我从未见过失败,而且我已经广泛使用它了。
答案 4 :(得分:1)
未提及的一个原因是中断信号可能会丢失,这使得调用Thread.interrupt()方法毫无意义。因此,除非Thread.run()方法中的代码在while循环中旋转,否则调用Thread.interrupt()的结果是不确定的。
答案 5 :(得分:0)
我注意到在线程ONE
中,当没有可用的数据库连接时(例如服务器关闭,因此最终该行抛出DriverManager.getConnection()
)并且从线程{{}执行SQLException
1}}我明确地调用TWO
,然后ONE.interrupt()
和ONE.interrupted()
都返回ONE.isInterrupted()
,即使作为false
块中的第一行放置catch{}
处理完毕。
当然我在实现额外信号量时遇到了这个问题,但它很麻烦,因为它是我15年Java开发中的第一个这样的问题。
我想这是因为SQLException
中的错误。我调查了更多以确认对com.microsoft.sqlserver.jdbc.SQLServerDriver
函数的调用在它所有的情况下消耗此异常,但在成功时保留它。
托梅克
P.S。我找到了analogous issue。
P.P.S
我附上了我上面写的一个很简短的例子。已注册的课程可在native
中找到。我在2015-08-20构建的课程中发现了这个错误,然后我更新到了最新版本(2017-01-12)并且错误仍然存在。
sqljdbc42.jar
如果您将某些完全错误的内容(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"
,您将收到消息"找不到适合foo"的驱动程序,第二个打印输出将是人们的期望仍然如此。但是,如果您传递正确构建的字符串,但是,例如,您的服务器已关闭或您丢失了网络连接(通常可能出现在生产环境中),您将看到DriverManager.getConnection()
套接字超时错误打印输出和线程&# 39; s java.net
状态很糟糕。
答案 6 :(得分:-3)
问题不在于实现不是错误的,而是当线程被中断时你的线程处于未知状态,这可能导致不可预测的行为。