【问题标题】:Does the finally block execute if the thread running the function is interrupted?如果运行函数的线程被中断,finally 块是否会执行?
【发布时间】:2013-01-29 06:16:03
【问题描述】:

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

【问题讨论】:

  • 你应该弄清楚你所说的“中断”是什么意思——这个词被重载了:(对于固件/驱动程序开发人员、操作系统开发人员和 java 开发人员来说意味着不同的东西。
  • 好吧,我将问题标记为“java”,并提到了 try/finally,所以我想人们可以理解我在谈论中断 Java 中的线程......

标签: java multithreading try-catch interrupt interruption


【解决方案1】:

According to the Java Tutorials, "如果执行trycatch 代码的线程被中断或杀死,即使整个应用程序继续运行,finally 块也可能不会执行。"

这是全文:

finally总是try 块退出时执行。这 确保 finally 块被执行,即使发生意外 发生异常。但是finally 不仅仅对异常有用 处理——它允许程序员避免清理代码 意外被returncontinuebreak 绕过。进行清理 finally 块中的代码始终是一个好习惯,即使没有 预计会有例外情况。

注意:如果在执行trycatch 代码时JVM 退出,那么 finally 块可能无法执行。同样,如果线程正在执行 trycatch 代码被中断或杀死,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();

它打印 - 最终执行

【讨论】:

  • 嗯...我可以理解,如果线程被杀死,最终不会被执行。但是,被中断的线程应该NOT 停止finally 子句的执行。我不知道为什么这段话这么说。以下是完整的 JLS 描述:docs.oracle.com/javase/specs/jls/se7/html/…
  • 好的,你的意思是即使线程被中断,最终 DOES 也会被执行,对吗?如果是这样,你的答案需要澄清。如果线程被中断,听起来好像 finally 可能不会执行。
  • 有人认为它是指绑定 java 线程的本机线程在系统级别被中断,而不是 java 线程中断。
  • 其实它只是因为捕获了中断才起作用。您没有正确处理 InterruptedException,因为您只是在吞咽它。您应该通过重新抛出或调用 Thread.currentThread.interrupt() 来传播它。有关详细信息,请参阅ibm.com/developerworks/java/library/j-jtp05236/index.html。那么问题是 - finally 还会被执行吗?
  • @Risadinha:恢复中断标志不能阻止 finally 块在这里执行。它只会影响该线程运行的任何其他代码是否可以检测到该线程是否已被中断。没有它不会影响 Subhrajyoti 测试的有效性。
【解决方案2】:

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

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

【讨论】:

  • @Joe 一个“控制”线程如何导致另一个线程在问题范围内“终止”,这是您可以在 Java 语言中执行的操作?即使是已弃用且备受诟病的 thread.stop 也会导致停止的线程进入 finally 块。
【解决方案3】:

commentsanswer 中,@Risadinha 提出了一个非常有效的问题,即如果我们通过调用Thread.currentThread().interrupt() 来恢复catch 块内的中断标志,finally 块中的代码是否会被执行。

这里是小代码sn-p来测试:

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

【讨论】:

  • 感谢您的测试。尽管如此,我仍然不会在 finally 块中放置任何关键任务代码,因为 Java 教程指出,如果线程被中断或终止,则无法保证它会执行。可能取决于 JVM/供应商/版本和/或资源等。
【解决方案4】:

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

首先,Java 中的线程中断与操作系统级别的中断无关。 Sharing a name creates opportunities for confusion but there is no connection.

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

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

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

【讨论】:

  • @Joe:阅读了很多书籍,比如 Java Concurrency in Practice,并亲自尝试了这些东西。有没有什么特别想知道它来自哪里的东西?
【解决方案5】:

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

【讨论】:

    【解决方案6】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-01
      • 2011-02-18
      相关资源
      最近更新 更多