gpt4 book ai didi

java - 理解Java中的线程中断

转载 作者:行者123 更新时间:2023-12-02 23:09:55 27 4
gpt4 key购买 nike

我正在阅读 this 文章中的线程中断。它说如下:

Before a blocking code throws an InterruptedException, it marks the interruption status as false. Thus, when handling of the InterruptedException is done, you should also preserve the interruption status by callingThread.currentThread().interrupt().

让我们看看这些信息如何应用于下面的示例。在提交给 ExecutorService 的任务中, printNumbers() 方法被调用两次。当任务被 toshutdownNow() 调用中断时,对该方法的第一次调用会提前完成,然后执行到第二次调用。中断仅由主线程调用一次。通过在第一次执行期间调用 printNumber(),将中断传达给 Thread.currentThread().interrupt() 方法的第二次执行。因此,第二次执行也会在打印第一个数字后提前完成。不保留中断状态将导致该方法的第二次执行完全运行 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;
}
}
}

我尝试运行上面的代码并打印:

01230

当我评论 Thread.currentThread().interrupt(); block 中的 catch 时,它​​会打印:

01230123456789

虽然我觉得我在代码之前理解了解释,但我不认为我理解为什么解释是正确的,部分原因是我在官方文档中没有找到任何相关的解释/句子。以下是我的具体疑问:

  1. 这是否意味着我们需要提交给 RunnableExecutorService.submit() 中的每个方法调用都必须捕获 InterruptedException 并在其中调用 Thread.currentThread().interrupt() 以便对整个 Runnable 主体进行中断?如果我们在 InterruptedException 中的任何方法调用中错过捕获 Thread.currentThread().interrupt() 并调用 Runnable ,那么它不会被中断吗?

  2. 如果上述正确,为什么 doc of Thread.interrupt() 中没有这样的解释?

  3. 如果我们想在任何方法中的中断时执行任何关闭任务,那么我们应该捕获 InterruptedException ,执行任何要完成的任务,然后调用 Thread.currentThread().interrupt() 吗?

  4. 如果问题3的答案是肯定的,那么我们什么时候应该做关闭任务?我觉得任何关闭任务都应该在 Thread.currentThread().interrupt() 之前完成,但在给定的示例中, breakThread.currentThread().interrupt() 之后调用。

  5. 再思考一下问题4,我觉得我不太明白线程中断是如何处理的。早些时候,我觉得中断的线程会立即被杀死,但情况似乎并非如此。线程中断是如何发生的?有没有oracle官方链接解释同样的事情?

最佳答案

对线程中断的最大误解是它是开箱即用的。是的,设置线程中断标志的基 native 制可用于处理线程中断,但线程应如何执行此操作仍取决于开发人员。请参阅 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.

例如:

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

虽然执行器被关闭并且线程被中断,但它打印:

01234567890123456789

这是因为没有任何线程中断处理。

例如,如果您使用 Thread.interrupted() 测试线程是否被中断,那么您实际上可以处理线程内的中断。

要在第一个 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);
}
}
}

这将执行第一个 printNumbers 并打印:

0123456789

您的示例的问题在于,每当您捕获 InterruptedException 时,线程的中断标志就会重置为 false

因此,如果您执行:

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

这将中断对 printNumbers 的第一次调用,但由于捕获了 InterruptedException,线程不再被视为中断,因此第二次执行 printNumbers 将继续,并且 sleep 不再被中断。输出将是:

0120123456789

但是,当您在 catch 后手动设置中断标志时,在第二次执行 printNumbers 期间,sleep 由于中断标志为 true 而被中断,并且您立即跳出循环:

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

它从第一次 printNumbers 执行中打印 012 ,从第二次 printNumbers 执行中打印 0 :

0120

但请记住,这只是因为您手动实现线程中断处理,在本例中是由于使用了 Thread.sleep。如最后一个示例所示:

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

这将打印出:

0120123456789

这是因为 printNumbersNotInterruptible 不处理线程中断,因此完成了其完整执行。

关于java - 理解Java中的线程中断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60905869/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com