Java问题:据我所知,在线程内部检查线程是否收到中断信号的方法有两种,即Thread.interrupted()
和Thread.isInterrupted()
,它们之间唯一的区别在于前者会重置内部的中断标志。
目前为止,我一直使用Thread.isInterrupted()
,并且从未遇到过任何问题。然而,大多数教程都建议使用Thread.interrupted()
。这是有特定原因吗?
Java问题:据我所知,在线程内部检查线程是否收到中断信号的方法有两种,即Thread.interrupted()
和Thread.isInterrupted()
,它们之间唯一的区别在于前者会重置内部的中断标志。
目前为止,我一直使用Thread.isInterrupted()
,并且从未遇到过任何问题。然而,大多数教程都建议使用Thread.interrupted()
。这是有特定原因吗?
interrupted()
方法是static
的,用于检查当前线程。而isInterrupted()
方法是一个实例方法,用于检查调用它的Thread
对象。
常见错误是在实例上调用静态方法。
Thread myThread = ...;
if (myThread.interrupted()) {} // WRONG! This might not be checking myThread.
if (myThread.isInterrupted()) {} // Right!
另一个区别是interrupted()
还会清除当前线程的状态。换句话说,如果你连续两次调用它并且在两次调用之间线程没有被中断,第二次调用即使第一次调用返回了true
,也会返回false
。
Javadocs告诉你这样重要的事情; 经常使用它们!
interrupted
,那么你所询问的是“自上次询问以来是否已被打断?”
isInterrupted
告诉你调用它的线程当前是否被打断。有很多与InterruptedException
相关的习语,但问题是关于显式检查中断状态。
我的理解是,实例方法isInterrupted
应该很少使用 - 主要用于记录和调试等。它只给出给定线程上标志的快照,很快就可能过时。
正常的习惯是在编写希望在某个点可取消的任务时检查静态方法interrupted
,此时它不会调用由于休眠或阻塞I/O调用等而引发InterruptedException
的内容。如果看到设置了标志,您应该尽快停止当前计算,提前返回或抛出异常(例如InterruptedException
)。
因此,举个例子,如果您的任务看起来像这样
void process(Things[] things) throws InterruptedException {
for (Thing thing : things) {
thing.twiddle(); // this call throws InterruptedException
}
}
如果没有其他需要,那么您不需要做任何事情;如果有人在您的线程上调用Thread.interrupt
,在当前或下一个twiddle
调用期间,将抛出InterruptedException
并停止您的任务。
但是,如果twiddle
没有抛出InterruptedException
并且通常无法在中途被中断怎么办?假设每个这样的调用需要100毫秒,但things.length
可能为100。那么即使有人试图中断它,在某些情况下,process
也可能被阻塞10秒钟,这在您的应用程序中可能是不可接受的。因此,您可以显式地检查中断:
void process(Things[] things) {
if (Thread.interrupted()) {
return;
}
for (Thing thing : things) {
thing.twiddle();
}
}
interrupted
需要原子性地检查和清除标志:你正在使用它来确认是否已经收到了一条消息,并且有人礼貌地请求你尽快停止。在这种情况下,应该在请求后的大约100毫秒内停止。你也可以看到为什么这必须是一个静态方法,操作当前线程:只有在检查周围代码是否应该停止的上下文中才有意义。process
的调用者假定它已经运行完毕,那么像这里展示的简单地return
将会是误导性的。所以你可能想让process
返回它完成处理的事物数量,或者直接向上抛出异常可能更合适。void process(Things[] things) throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
for (Thing thing : things) {
thing.twiddle();
}
}
void process(Things[] things) {
boolean twiddleFully = true;
if (twiddleFully && Thread.interrupted()) {
twiddleFully = false;
Thread.currentThread().interrupt();
}
for (Thing thing : things) {
thing.twiddle(twiddleFully);
}
}
在这里,我们可以更快地处理剩余的事情,但仍然完成循环,并打开中断标志,以便我们的调用者可以决定如何处理它。
interrupted()
方法是一个类方法,它始终检查当前线程并清除中断 "标志"。换句话说,对 interrupted()
的第二次调用将返回 false
。
isInterrupted()
方法是一个实例方法;它报告调用它的线程的状态。此外,它不会清除中断标志。如果标志已设置,则在调用此方法后仍将保持设置。如果你正在编写自己的线程池,可能需要检查其中一个管理的线程是否已被中断。这种情况下,你可以调用 managedThread.isInterrupted()
来检查它的中断状态。
如果你编写了自己的 InterruptedException 处理程序,并且不立即通过 Thread.currentThread().interrupt()
触发等效异常(例如,在异常处理程序之后你可能有一个 finally 块),你可能希望检查当前运行的线程是否已经被外部调用或 InterruptedException 中断。在这种情况下,你应该检查 Thread.interrupted()
的布尔值以检查当前线程的状态。
interrupted()方法是Thread类的静态方法,用于检查当前线程并清除中断标志。也就是说,对interrupted()方法的第二次调用将返回false。
isInterrupted()方法是实例方法,用于报告调用它的线程的状态。它不会清除中断标志。
如果标志被设置,在调用此方法后,它将保持设置状态。
Thread myThread = ...;
if (myThread.interrupted()) {} //error
Thread.interrupted()//right
if (myThread.isInterrupted()) {} // Right
这是一个老问题,经过查看答案后,我感觉仍然有一些信息缺失。以下是我填补那个缺失信息的尝试。
从Java 5开始,通常您只会间接地处理线程。实际上,从java.util.Executor框架生成的线程在库方法中处理。这些线程经常调用阻塞性质的实体,例如Future.get()
。也就是说,get()
会一直阻塞,直到结果可用为止。现在有一个重载形式的get()
,它需要一个超时值,并且调用该方法意味着线程想要等待一个等于超时时间的期间,以便get()
返回一个值,如果没有,那么可以通过Future.cancel()取消该任务。因此,这些方法认真处理中断,一旦它们嗅到中断,它们也会抛出已检查的InterruptionException。因此,调用者被迫处理InterruptionException。由于它们已经传播了传达中断状态的InterruptedException,因此让阻塞方法通过调用Thread.interrupt()来清除中断状态是有意义的。否则,将违反InterruptedException的契约。
然而,如果您正在处理原始线程(这当然现在不推荐),则在调用静态方法interrupted()
时应该小心,因为如果您连续调用两次并且在线程两次调用之间未被中断,则第二次调用将返回false,即使第一次调用返回true。
Thread.interrupted()
时,它会将值设置为false
,以便下次调用时您将看到该值为false
。请参见:http://www.yegor256.com/2015/10/20/interrupted-exception.html - Dave YarwoodThread.interrupted()
并且它返回 true
时,最好停止正在进行的操作,关闭并退出线程。如果无法退出,则最好通过 Thread.currentThread().interrupt()
重新中断线程,以便调用者可以处理中断。否则,调用者将不知道线程已被中断! - Dave Yarwood为什么要中断?
在Java中,当你需要停止一个长时间运行的任务或关闭守护进程等情况时,中断线程是非常有用的。
如何中断
要中断线程,你需要在该线程上调用interrupt()方法。这是一个协作过程,因此你的代码必须准备好接受中断。例如:
myThread.interrupt();
负责任的代码
你的代码的职责是准备好应对任何中断。我甚至可以说,每当你有一个长时间运行的任务时,都要插入一些类似于这样的中断就绪代码:
while (... something long...) {
... do something long
if (Thread.interrupted()) {
... stop doing what I'm doing...
}
}
如何停止正在进行的操作?
您有几个选项:
private void someMethodDeepDown() {
while (.. long running task .. ) {
... do lots of work ...
if (Thread.interrupted()) {
// oh no! an interrupt!
Thread.currentThread().interrupt();
throw new SomeOtherException();
}
}
}
中断是最好的中断机制,因为我们的代码可以做好准备。如果我们发现代码只是捕获和忽略InterruptException或者在其主体中没有检查interrupted(),那么我们可以纠正这些错误,并使我们的代码始终能够干净地中断,而不会在您的代码中创建奇怪的依赖关系非标准机制。
不幸的是,Joshua Block在他著名的书籍Effective Java, Second Edition中提出了相反的观点。但我认为启用interrupt()方法按预期工作要好得多。
Future.cancel()已经处理了这个问题,对吗?
Future cancel将任务从运行队列中删除。如果您的任务已经在运行,则不会停止它。因此,cancel()与中断是不同的概念。如Javadocs所述:
尝试取消执行此任务。如果任务已经完成、已被取消或由于其他原因无法取消,则此尝试将失败。如果成功,且在调用取消时此任务尚未启动,则此任务不应运行。如果任务已经启动,则 mayInterruptIfRunning 参数确定是否应中断执行此任务的线程以尝试停止任务。但是,如果 mayInterruptIfRunning 是开启的,则调用将生成一个中断。 https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/concurrent/Future.html#cancel(boolean)