23

如果我有一个带有 try/finally 部分的函数,并且运行它的线程在 try 块中被中断,finally 块会在中断实际发生之前执行吗?

4

6 回答 6

12

根据 Java 教程,“如果执行tryorcatch代码的线程被中断或杀死,finally即使应用程序作为一个整体继续运行,该块也可能不会执行。”

这是完整的段落:

finally总是try在块退出时执行。这可确保finally即使发生意外异常也能执行该块。但finally它不仅仅对异常处理有用——它允许程序员避免清理代码意外地被returncontinuebreak. 将清理代码放在一个finally块中始终是一个好习惯,即使没有预料到异常。

注意:try如果在执行orcatch代码时 JVM 退出,则该finally块可能不会执行。同样,如果执行tryorcatch代码的线程被中断或杀死,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();

它打印- 最终执行

于 2013-01-29T06:18:21.503 回答
4

Java中的线程中断只是设置一个标志。它不会导致当前执行的代码发生任何特殊情况,也不会影响控制流。

如果您的线程正在从事或尝试进入引发 InterruptedException 的操作,则从调用该方法的位置引发异常,如果它位于 try 块内,则 finally 将在异常离开之前像正常一样执行.

于 2013-01-29T06:22:20.947 回答
4

在对答案的评论中,@Risadinha 提出了一个非常有效的问题,即如果我们通过调用恢复块内的中断标志,块中的代码是否会被执行。finallycatchThread.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;
}
于 2015-10-30T12:51:43.093 回答
4

Oracle 的许多 Java 教程很有帮助(我的答案参考了受保护的块页面和 SAX 介绍),但它们不一定具有权威性,其中一些有错误或不完整。问题中引用的引用将中断与 JVM 退出混为一谈,这令人困惑。

首先,Java 中的线程中断与操作系统级别的中断无关。共享一个名字会造成混淆,但没有任何联系

接下来,JVM exit 显然会在没有机会进行任何清理的情况下杀死线程。如果进程在线程到达 finally 块之前就死掉了,那就太糟糕了。但没有什么能与中断相提并论。中断并没有阻止 finally 块完成。

中断的一个设计原则是作用于中断需要被中断线程的配合。被中断的线程自行决定响应,中断不会强迫线程做任何事情。所有调用 Thread#interrupt() 所做的都是在线程上设置一个标志。诸如等待或睡眠之类的阻塞方法检查标志以查看它们是否应该早起。(InterruptedException 是一个经过检查的异常,因此您可以知道谁在何时抛出它,并且您的 Runnable 可以对此进行计划。)此外,任何代码都可以使用 Thread#isInterrupted() 来检查其线程是否已设置标志。

当 Thread#sleep() 识别到设置了中断标志时,它会在抛出 InterruptedException 之前清除该标志。当您的线程捕获到 InterruptedException 时,最好使用 Thread.currentThread().interrupt() 恢复标志,以防万一该线程中运行的任何其他代码需要了解中断。当嵌套同步器遇到更复杂的情况时,这就会发挥作用,例如,一些深度嵌套的组件可能会中断其睡眠,让它保持清除可能会阻止更高层了解中断。在一个简单的玩具示例中,就像此处其他答案中的示例一样,标志是否恢复并不重要,没有任何东西再次检查它并且线程终止。

于 2015-10-30T16:50:44.520 回答
1

中断的效果是InterruptedException在下一次发生阻塞操作时抛出一个(实际上,下一次调用指定它可以抛出一个方法的方法InterruptedException),此时 - 像往常一样 -try/catch遵循正常的执行流程,它确实在和任何适用的esfinally之后执行块。trycatch

于 2013-01-29T06:18:19.063 回答
0

它将以与 try 块中的任何其他异常相同的方式执行,而不是在中断之前。

于 2013-01-29T06:29:45.310 回答