java Thread.interrupt() 是邪恶的吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2020992/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 18:57:48  来源:igfitidea点击:

Is Thread.interrupt() evil?

javamultithreadinginterrupt

提问by ripper234

A teammate made the following claim:

一位队友提出了以下主张:

"Thread.interrupt()is inherently broken, and should (almost) never be used".

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

I am trying to understand why this is the case.

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

Is it a known best practice never to use Thread.interrupt()? Can you provide evidence why it is broken / buggy, and should not be used for writing robust multithreaded code?

永远不要使用是已知的最佳实践Thread.interrupt()吗?你能提供证据为什么它是坏的/有问题的,不应该用于编写健壮的多线程代码吗?

Note- I am not interested in this question if it's "pretty" from a design preservative. My question is - is it buggy?

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

回答by Bob Cross

Short version:

简洁版本:

Is it a known best practice never to use Thread.interrupt()?

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

No.

不。

Can you provide evidence why it is broken / buggie, and should not be used for writing robust multithreaded code?

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

The opposite is true: it is critical for multithreaded code.

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

See Listing 7.7 in Java Concurrency in Practicefor an example.

有关示例,请参见Java 并发实践中的代码清单 7.7 。

Longer version:

更长的版本:

Around here, we use this method in one specific place: handling InterruptedExceptions. That may seem a little strange but here's what it looks like in code:

在这里,我们在一个特定的地方使用这种方法:处理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();
}

This does two things for us:

这对我们有两件事:

  1. It avoids eating the interrupt exception. IDE auto-exception handlers always provide you with something like ie.printStackTrace();and a jaunty "TODO: Something useful needs to go here!" comment.
  2. It restores the interrupt status without forcing a checked exception on this method. If the method signature that you're implementing does not have a throws InterruptedExceptionclause, this is your other option for propagating that interrupted status.
  1. 它避免了吃中断异常。IDE 自动异常处理程序总是为您提供类似ie.printStackTrace();“TODO:这里需要一些有用的东西!”之类的东西。评论。
  2. 它在不强制此方法上检查异常的情况下恢复中断状态。如果您正在实现的方法签名没有throws InterruptedException子句,这是传播中断状态的另一种选择。

A commenter suggested that I should be using an unchecked exception "to force the thread to die." This is assuming that I have prior knowledge that killing the thread abruptly is the proper thing to do. I don't.

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

To quote Brian Goetz from JCIP on the page before the listing cited above:

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

A task should not assume anything about the interruption policy of its executing thread unless it is explicitly designed to run within a service that has a specific interruption policy.

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

For example, imagine that I did this:

例如,假设我这样做了:

} 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);
}

I would be declaring that, regardless of other obligations of the rest of the call stack and associated state, this thread needs to die right now. I would be trying to sneak past all the other catch blocks and state clean-up code to get straight to thread death. Worse, I would have consumed the thread's interrupted status. Upstream logic would now have to deconstruct my exception to try to puzzle out whether there was a program logic error or whether I'm trying to hide a checked exception inside an obscuring wrapper.

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

For example, here's what everyone else on the team would immediately have to do:

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

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();
    }
}

The interrupted state is more important than any specific InterruptException. For a specific example why, see the javadoc for Thread.interrupt():

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

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

如果此线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法或 join()、join(long)、join(long, int) 方法时被阻塞、 sleep(long) 或 sleep(long, int) 等方法,则其中断状态将被清除,并会收到 InterruptedException。

As you can see, more than one InterruptedException could get created and handled as interrupt requests are processed but only if that interrupt status is preserved.

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

回答by Sbodd

The only way I'm aware of in which Thread.interrupt()is broken is that it doesn't actually do what it seems like it might - it can only actually interruptcode that listens for it.

我知道哪个Thread.interrupt()被破坏的唯一方法是它实际上并没有像它看起来的那样做 - 它实际上只能中断侦听它的代码。

However, used properly, it seems to me like a good built-in mechanism for task management and cancellation.

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

I recommend Java Concurrency in Practicefor more reading on the proper and safe use of it.

我推荐Java Concurrency in Practice以阅读有关正确和安全使用它的更多信息。

回答by Aaron Digulla

The main problem with Thread.interrupt()is that most programmers don't know about the hidden pitfalls and use it in the wrong way. For example, when you handle the interrupt, there are methods which clearthe flag (so the status gets lost).

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

Also, the call will not always interrupt the thread right away. For example, when it hangs in some system routine, nothing will happen. In fact, if the thread doesn't check the flag and never calls a Java method which throws InterruptException, then interrupting it will have no effect whatsoever.

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

回答by Robert Munteanu

No, it's not buggy. It actually is the basis of how you stop threads in Java. It's used in the Executor framework from java.util.concurrent - see the implementation of java.util.concurrent.FutureTask.Sync.innerCancel.

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

As for failure, I've never seen it fail, and I've used it extensively.

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

回答by clinton

One reason not mentioned is that the interrupt signal can be lost which makes invoking the Thread.interrupt() method meaningless. So unless your code in the Thread.run() method is spinning in a while loop the outcome of calling Thread.interrupt() is uncertain.

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

回答by Tomek

I noticed that when in thread ONEI execute DriverManager.getConnection()when there is no database connection available (say server is down thus finally this line throws SQLException) and from the thread TWOI explicitely call ONE.interrupt(), then both ONE.interrupted()and ONE.isInterrupted()return falseeven if placed as the first line in the catch{}block where SQLExceptionis handled.

我注意到,当线程ONE我执行DriverManager.getConnection()的时候没有可用的数据库连接(比如服务器宕机从而最终这条线抛出SQLException),并从线程TWO我明确地调用ONE.interrupt(),那么这两个ONE.interrupted()ONE.isInterrupted()返回false如果放置的第一线,即使catch{}块地方SQLException是处理。

Of course I workarounded this issue implementing the extra semaphore but it is quite troublesome, as it is the very first such issue in my 15 years Java development.

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

I suppose it's because of bug in com.microsoft.sqlserver.jdbc.SQLServerDriver. And I investigated more to confirm that the call to the nativefunction consumes this interruption in all cases it trhows its own, but preserves it when succeeded.

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

Tomek

托梅克

P.S. I found the analogous issue.

PS我发现了类似的问题

P.P.S I enclose a very short example of what I'm writting above. The registered class can be found in sqljdbc42.jar. I found this bug in classes built on 2015-08-20 then I updated to the newest version available (from 2017-01-12) and the bug still exists.

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();
    }
}

If you pass something completely incorrect, as "foo", to the DriverManager.getConnection(), you will obtain the message "No suitable driver found for foo", and the second printout will be still true as one would expect. But if you pass the correctly built string but, say, your server is down or you lost your net connection (that can generally occurr in the production environment), you will see the java.netsocket timeout error printout and the thread's interrupted()state is LOST.

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

回答by Brian Ensink

THe problem is not that the implementation is not buggy but rather your thread is in an unknown state when it gets interrupted and this can lead to unpredictable behavior.

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