[英]Understanding thread interruption in Java
I was reading thread interrupting from this article.我正在阅读这篇文章中的线程中断。 It says following:
它说如下:
Before a blocking code throws an
InterruptedException
, it marks the interruption status as false.在阻塞代码抛出
InterruptedException
,它将中断状态标记为 false。 Thus, when handling of theInterruptedException
is done, you should also preserve the interruption status bycallingThread.currentThread().interrupt()
.因此,当处理完
InterruptedException
,您还应该通过callingThread.currentThread().interrupt()
保留中断状态。Let's see how this information applies to the example below.
让我们看看这些信息如何应用于下面的示例。 In the task that is submitted to the
ExecutorService
, theprintNumbers()
method is called twice.在提交给
ExecutorService
的任务中,printNumbers()
方法被调用两次。 When the task is interrupted by a calltoshutdownNow()
, the first call to the method finishes early and then the execution reaches the second call.当任务被调用
toshutdownNow()
中断时,对该方法的第一次调用提前结束,然后执行到达第二次调用。 The interruption is called by the main thread only once.中断只被主线程调用一次。 The interruption is communicated to the second execution of the
printNumber()
method by the call toThread.currentThread().interrupt()
during the first execution.在第一次执行期间,通过调用
Thread.currentThread().interrupt()
将中断传达给printNumber()
方法的第二次执行。 Hence the second execution also finishes early just after printing the first number.因此,第二次执行也会在打印第一个数字后提前结束。 Not preserving the interruption status would have caused the second execution of the method to run fully for 9 seconds.
不保留中断状态会导致方法的第二次执行完全运行 9 秒。
public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<?> future = executor.submit(() -> { printNumbers(); // first call printNumbers(); // second call }); Thread.sleep(3_000); executor.shutdownNow(); // will interrupt the task executor.awaitTermination(3, TimeUnit.SECONDS); } private static void printNumbers() { for (int i = 0; i < 10; i++) { System.out.print(i); try { Thread.sleep(1_000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // preserve interruption status break; } } }
I tried running above code and it prints:我尝试运行上面的代码并打印:
01230
When I comment Thread.currentThread().interrupt();
当我评论
Thread.currentThread().interrupt();
from catch
block, it prints:从
catch
块,它打印:
01230123456789
Though I feel I understand explanation before code, I dont think I understand why the explanation is correct, partly because I did not find any related explanation / sentence in the official doc.虽然我觉得我在代码之前理解了解释,但我认为我不明白为什么解释是正确的,部分原因是我在官方文档中没有找到任何相关的解释/句子。 Here are my specific doubts:
以下是我的具体疑问:
Does that mean we need every method call in Runnable
submitted to ExecutorService.submit()
to have catch InterruptedException
and call Thread.currentThread().interrupt()
in it for whole Runnable
body to interrpt?这是否意味着我们需要将
Runnable
每个方法调用提交给ExecutorService.submit()
来捕获InterruptedException
并在其中调用Thread.currentThread().interrupt()
以便整个Runnable
主体进行中断? If we miss to catch InterruptedException
and call Thread.currentThread().interrupt()
in any method call in Runnable
, then it will not be interrupted?如果我们在
Runnable
任何方法调用中错过捕获InterruptedException
并调用Thread.currentThread().interrupt()
,那么它不会被中断?
If above is correct, why there is not such explanation in the doc of Thread.interrupt()
?如果以上是正确的,为什么
Thread.interrupt()
的文档中没有这样的解释?
Is it like if we want to do any closure task on interruption in any method, then only we should catch InterruptedException
, perform any task to be done and then call Thread.currentThread().interrupt()
?是不是如果我们想在任何方法的中断上做任何关闭任务,那么我们应该只捕获
InterruptedException
,执行任何要完成的任务然后调用Thread.currentThread().interrupt()
?
If answer to question 3 is yes, then when we should be doing closure task?如果问题 3 的答案是肯定的,那么我们应该什么时候做关闭任务? I feel any closure task should be done before
Thread.currentThread().interrupt()
, but in the given example, break
is called after Thread.currentThread().interrupt()
.我觉得任何关闭任务都应该在
Thread.currentThread().interrupt()
之前完成,但在给定的例子中, break
在Thread.currentThread().interrupt()
之后被调用。
After thinking on question 4 a bit more, I feel I dont understand it clearly how thread interruption is handled.对问题4多想了几句,感觉线程中断是怎么处理的,没看清楚。 Earlier I felt interrupted thread simply gets killed immediately, but that does not seem to be the case.
早些时候我觉得被中断的线程会立即被杀死,但情况似乎并非如此。 How thread interruption occurs?
线程中断是如何发生的? Is there any oracle official link explaining the same?
是否有任何 oracle 官方链接解释相同?
Thread interruption is not something that kills a thread right away.线程中断不是立即杀死线程的事情。 It is a suggestion for the thread to end themself.
这是线程结束自己的建议。
The code in your thread will not terminate automatically.线程中的代码不会自动终止。 So your code should react to thread interuption by terminating gracefully.
所以你的代码应该通过优雅地终止来响应线程中断。 One good practice is to reset the interrupted flag everytime it was consumed, by calling
Thread.currentThread().interrupt()
.一种好的做法是在每次使用时重置中断标志,方法是调用
Thread.currentThread().interrupt()
。 Following operations that respect the status will throw InterruptedException, and will not just continue.遵循状态的操作将抛出 InterruptedException,并且不会继续。
Thread.currentThread().interrupt();
should be the first statement in the catch block, to make sure any cleanup doesnt block again.应该是 catch 块中的第一条语句,以确保任何清理不会再次阻塞。 (Using finally for cleanup is also a good idea)
(使用 finally 进行清理也是一个好主意)
In that specific case I would have pulled the try / catch out of the for loop.在那种特定情况下,我会将 try / catch 拉出 for 循环。
It is basically a suggestion for the thread to end itself.它基本上是线程结束自身的建议。
Java language specification 17.2.3
Java 语言规范 17.2.3
Interruption actions occur upon invocation of Thread.interrupt, as well as methods defined to invoke it in turn, such as ThreadGroup.interrupt.
中断操作发生在调用 Thread.interrupt 时,以及定义为依次调用它的方法,例如 ThreadGroup.interrupt。
Let t be the thread invoking u.interrupt, for some thread u, where t and u may be the same.
设 t 为调用 u.interrupt 的线程,对于某些线程 u,其中 t 和 u 可能相同。 This action causes u's interruption status to be set to true.
此操作会导致您的中断状态设置为 true。
Additionally, if there exists some object m whose wait set contains u, then u is removed from m's wait set.
此外,如果存在某个对象 m 的等待集包含 u,则将 u 从 m 的等待集中删除。 This enables u to resume in a wait action, in which case this wait will, after re-locking m's monitor, throw InterruptedException.
这使您能够在等待操作中恢复,在这种情况下,此等待将在重新锁定 m 的监视器后抛出 InterruptedException。
Invocations of Thread.isInterrupted can determine a thread's interruption status.
调用 Thread.isInterrupted 可以确定线程的中断状态。 The static method Thread.interrupted may be invoked by a thread to observe and clear its own interruption status.
线程可以调用静态方法 Thread.interrupted 来观察和清除自己的中断状态。
- ——
Oracle Tutorial about interrupts
Oracle 中断教程
The Interrupt Status Flag The interrupt mechanism is implemented using an internal flag known as the interrupt status.
中断状态标志 中断机制是使用称为中断状态的内部标志实现的。 Invoking Thread.interrupt sets this flag.
调用 Thread.interrupt 设置此标志。 When a thread checks for an interrupt by invoking the static method Thread.interrupted, interrupt status is cleared.
当线程通过调用静态方法 Thread.interrupted 检查中断时,中断状态被清除。 The non-static isInterrupted method, which is used by one thread to query the interrupt status of another, does not change the interrupt status flag.
一个线程使用非静态 isInterrupted 方法来查询另一个线程的中断状态,它不会更改中断状态标志。
By convention, any method that exits by throwing an InterruptedException clears interrupt status when it does so.
按照惯例,任何通过抛出 InterruptedException 退出的方法都会在它这样做时清除中断状态。 However, it's always possible that interrupt status will immediately be set again, by another thread invoking interrupt.
然而,中断状态总是有可能被另一个调用中断的线程立即再次设置。
The biggest misconception with thread interruption is that this works out of the box.对线程中断的最大误解是它是开箱即用的。 Yes, the basic mechanics to set the interrupt flag of a thread are made available to deal with thread interruption, but how a thread should do this is still up to the developer.
是的,设置线程中断标志的基本机制可以用来处理线程中断,但是线程应该如何做到这一点仍然取决于开发人员。 See the note on ExecutorService
shutdownNow
method:参见 ExecutorService
shutdownNow
方法的注释:
There are no guarantees beyond best-effort attempts to stop processing actively executing tasks.
除了尽力尝试停止处理正在执行的任务之外,没有任何保证。 For example, typical implementations will cancel via Thread.interrupt, so any task that fails to respond to interrupts may never terminate.
例如,典型的实现将通过 Thread.interrupt 取消,因此任何未能响应中断的任务可能永远不会终止。
For example:例如:
public class Hello {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
printNumbers(); // first call
printNumbers(); // second call
});
executor.shutdownNow(); // will interrupt the task
executor.awaitTermination(3, TimeUnit.SECONDS);
}
private static void printNumbers() {
for (int i = 0; i < 10; i++) {
System.out.print(i);
}
}
}
Although the executor was shutdown and the thread was interrupted, it prints:虽然执行程序被关闭并且线程被中断,但它打印:
01234567890123456789
This is because there is no thread interruption handling whatsoever.这是因为没有任何线程中断处理。
If you would for example test if the thread was interrupted using Thread.interrupted()
, then you can actually deal with an interruption within your thread.例如,如果您要使用
Thread.interrupted()
测试线程是否被中断,那么您实际上可以处理线程内的中断。
To halt after the first printNumbers
you can do:要在第一个
printNumbers
之后停止,您可以执行以下操作:
public class Hello {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
printNumbers(); // first call
// this checks the interrupt flag of the thread, which was set to
// true by calling 'executor.shutdownNow()'
if (Thread.interrupted())
throw new RuntimeException("My thread got interrupted");
printNumbers(); // second call
});
executor.shutdownNow(); // will interrupt the task
executor.awaitTermination(3, TimeUnit.SECONDS);
}
private static void printNumbers() {
for (int i = 0; i < 10; i++) {
System.out.print(i);
}
}
}
This wil execute the first printNumbers
and print:这将执行第一个
printNumbers
并打印:
0123456789
The problem with your example is that whenever you catch InterruptedException
, the interrupt flag of the thread is reset to false
.您的示例的问题在于,无论何时捕获
InterruptedException
,线程的中断标志都会重置为false
。
Therefore, if you would execute:因此,如果您要执行:
public class Hello {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
printNumbers(); // first call
printNumbers(); // second call
});
Thread.sleep(3_000);
executor.shutdownNow(); // will interrupt the task
executor.awaitTermination(3, TimeUnit.SECONDS);
}
private static void printNumbers() {
for (int i = 0; i < 10; i++) {
System.out.print(i);
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
// the interrupt flag is reset, but do not do anything else
// except break from this loop
break;
}
}
}
}
This wil interrupt the first call to printNumbers
, but due to the catch of InterruptedException
, the thread is not considered interrupted anymore and therefore the second execution of printNumbers
will continue and the sleep
is never interrupted anymore.这将中断对
printNumbers
的第一次调用,但由于捕获了InterruptedException
,线程不再被视为中断,因此printNumbers
的第二次执行将继续并且sleep
不再被中断。 The output will be:输出将是:
0120123456789
However, when you manually set back the interrupt flag after the catch, during the second execution of printNumbers
the sleep
is interrupted due to the interrupt flag being true and you immediately break out of the loop:但是,当您在捕获后手动设置中断标志时,在第二次执行
printNumbers
期间,由于中断标志为真, sleep
被中断,您立即跳出循环:
public class Hello {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
printNumbers(); // first call
printNumbers(); // second call
});
Thread.sleep(3_000);
executor.shutdownNow(); // will interrupt the task
executor.awaitTermination(3, TimeUnit.SECONDS);
}
private static void printNumbers() {
for (int i = 0; i < 10; i++) {
System.out.print(i);
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
// the interrupt flag is reset to false, so manually set it back to true
// and then break from this loop
Thread.currentThread().interrupt();
break;
}
}
}
}
It prints 012
from the first printNumbers
execution and 0
from the second printNumbers
execution:它从第一次
printNumbers
执行中打印012
,从第二次printNumbers
执行中打印0
:
0120
But remember, this is only because you are manually implementing thread interruption handling, in this case due to using Thread.sleep
.但请记住,这只是因为您正在手动实现线程中断处理,在这种情况下是由于使用
Thread.sleep
。 As shown by this last example:如最后一个示例所示:
public class Hello {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
printNumbers(); // first call
printNumbersNotInterruptible(); // second call
});
Thread.sleep(3_000);
executor.shutdownNow(); // will interrupt the task
executor.awaitTermination(3, TimeUnit.SECONDS);
}
private static void printNumbers() {
for (int i = 0; i < 10; i++) {
System.out.print(i);
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
private static void printNumbersNotInterruptible() {
for (int i = 0; i < 10; i++) {
System.out.print(i);
}
}
}
This will print out:这将打印出:
0120123456789
This is because printNumbersNotInterruptible
does not deal with thread interruption and therefore completes its full execution.这是因为
printNumbersNotInterruptible
不处理线程中断并因此完成其完整执行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.