如果我有一个带有 try/finally 部分的函数,并且运行它的线程在 try 块中被中断,finally 块会在中断实际发生之前执行吗?
6 回答
根据 Java 教程,“如果执行try
orcatch
代码的线程被中断或杀死,finally
即使应用程序作为一个整体继续运行,该块也可能不会执行。”
这是完整的段落:
该
finally
块总是try
在块退出时执行。这可确保finally
即使发生意外异常也能执行该块。但finally
它不仅仅对异常处理有用——它允许程序员避免清理代码意外地被return
、continue
或break
. 将清理代码放在一个finally
块中始终是一个好习惯,即使没有预料到异常。注意:
try
如果在执行orcatch
代码时 JVM 退出,则该finally
块可能不会执行。同样,如果执行try
orcatch
代码的线程被中断或杀死,finally
即使应用程序作为一个整体继续运行,该块也可能不会执行。
class Thread1 implements Runnable {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("finally executed");
}
}
}
...
t1.start();
t1.interrupt();
它打印- 最终执行
Java中的线程中断只是设置一个标志。它不会导致当前执行的代码发生任何特殊情况,也不会影响控制流。
如果您的线程正在从事或尝试进入引发 InterruptedException 的操作,则从调用该方法的位置引发异常,如果它位于 try 块内,则 finally 将在异常离开之前像正常一样执行.
在对答案的评论中,@Risadinha 提出了一个非常有效的问题,即如果我们通过调用恢复块内的中断标志,块中的代码是否会被执行。finally
catch
Thread.currentThread().interrupt()
这是要测试的小代码片段:
final SomeContext context = new SomeContext();
Thread thread = new Thread() {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// this code gets executed even though
// we interrupt thread in catch block.
context.value = 9;
}
}
};
thread.start();
thread.interrupt();
thread.join(); // need to wait for thread code to complete
assertEquals(context.value, 9); // values are the same!
SomeContext 类代码:
class SomeContext {
public volatile int value = 10;
}
Oracle 的许多 Java 教程很有帮助(我的答案参考了受保护的块页面和 SAX 介绍),但它们不一定具有权威性,其中一些有错误或不完整。问题中引用的引用将中断与 JVM 退出混为一谈,这令人困惑。
首先,Java 中的线程中断与操作系统级别的中断无关。共享一个名字会造成混淆,但没有任何联系。
接下来,JVM exit 显然会在没有机会进行任何清理的情况下杀死线程。如果进程在线程到达 finally 块之前就死掉了,那就太糟糕了。但没有什么能与中断相提并论。中断并没有阻止 finally 块完成。
中断的一个设计原则是作用于中断需要被中断线程的配合。被中断的线程自行决定响应,中断不会强迫线程做任何事情。所有调用 Thread#interrupt() 所做的都是在线程上设置一个标志。诸如等待或睡眠之类的阻塞方法检查标志以查看它们是否应该早起。(InterruptedException 是一个经过检查的异常,因此您可以知道谁在何时抛出它,并且您的 Runnable 可以对此进行计划。)此外,任何代码都可以使用 Thread#isInterrupted() 来检查其线程是否已设置标志。
当 Thread#sleep() 识别到设置了中断标志时,它会在抛出 InterruptedException 之前清除该标志。当您的线程捕获到 InterruptedException 时,最好使用 Thread.currentThread().interrupt() 恢复标志,以防万一该线程中运行的任何其他代码需要了解中断。当嵌套同步器遇到更复杂的情况时,这就会发挥作用,例如,一些深度嵌套的组件可能会中断其睡眠,让它保持清除可能会阻止更高层了解中断。在一个简单的玩具示例中,就像此处其他答案中的示例一样,标志是否恢复并不重要,没有任何东西再次检查它并且线程终止。
中断的效果是InterruptedException
在下一次发生阻塞操作时抛出一个(实际上,下一次调用指定它可以抛出一个方法的方法InterruptedException
),此时 - 像往常一样 -try/catch
遵循正常的执行流程,它确实在和任何适用的esfinally
之后执行块。try
catch
它将以与 try 块中的任何其他异常相同的方式执行,而不是在中断之前。