【问题标题】:Does a finally block always run?finally 块是否总是运行?
【发布时间】:2009-01-21 04:38:24
【问题描述】:

是否有任何情况下 finally 可能无法在 java 中运行?谢谢。

【问题讨论】:

  • 这是你在某家知名公司找工作时可能会被问到的问题吗?
  • @TomHawtin-tackline 想命名它吗? (上帝,我错过了这篇文章的历史!)
  • @Hele 我不想放弃游戏,但你可以用谷歌搜索。
  • 简短回答:是的,在正常情况下。

标签: java finally


【解决方案1】:

来自Sun Tutorials

注意:如果 JVM 在尝试时退出 或捕获代码正在执行,然后 finally 块可能不会执行。 同样,如果线程执行 try 或 catch 代码被中断或 被杀了,finally块可能不会 即使应用程序作为 整体继续。

我不知道 finally 块不会执行的任何其他方式...

【讨论】:

  • @dhiller - 我很确定“如果 JVM 退出...”中包含“断电”:-p
  • @Jason Coco:终止(如断电)与退出并不完全相同;后者是一个或多或少有组织的过程,最终导致前者。 ;p
  • AFAIK,如果线程被中断,它不会立即停止。由线程中的代码检测中断并停止其任务,因此最终代码应该运行。
  • 我猜如果finally块中抛出异常,该块的其余部分不会被执行。
  • 那太糟糕了!
【解决方案2】:

System.exit 关闭虚拟机。

终止当前运行的 Java 虚拟机。论据服务 作为状态码;按照惯例,一个 非零状态码表示异常 终止。

这个方法调用exit方法在 类Runtime。这种方法从来 正常返回。

    try {
        System.out.println("hello");
        System.exit(0);
    }
    finally {
        System.out.println("bye");
    } // try-finally

“再见”在上面的代码中没有打印出来。

【讨论】:

  • 还有一个异常应该关闭 Java 虚拟机。
  • 如果在执行 System.exit(0) 时发生异常,那么 finally 块将执行。
【解决方案3】:

只是为了扩展其他人所说的,任何不会导致诸如 JVM 退出之类的事情都会导致 finally 块。所以下面的方法:

public static int Stupid() {
  try {
    return 0;
  }
  finally {
    return 1;
  }
}

会奇怪地编译并返回 1。

【讨论】:

  • 几周前这真的让我困惑了好几个小时。
  • 从 finally 块返回值被认为是一个坏主意。要么只从 try 块返回,要么从 try/finally 块之外返回。大多数 IDE 都会用警告标记。
  • @nickf 我猜你不再困惑了。您能否详细说明为什么返回 1 而不是 0 的机制。我只能猜测存储最初保存 0 的函数的返回值的内存(或者是寄存器)在执行 finally 块时被覆盖.
  • 这很好奇,在 C# 中不允许从 finally 块返回。
  • @RanBiron 当然。他实际上并不是建议在 finally 块中返回,他只是想证明即使是 return 语句仍然会导致该块中的代码执行。
【解决方案4】:

与 System.exit 相关,还有某些类型的灾难性故障可能无法执行 finally 块。如果 JVM 完全耗尽内存,它可能会直接退出而没有捕获或最终发生。

具体来说,我记得一个我们愚蠢地尝试使用的项目

catch (OutOfMemoryError oome) {
    // do stuff
}

这不起作用,因为 JVM 没有剩余内存可用于执行 catch 块。

【讨论】:

  • 当 OutOfMemoryError 被抛出时,通常会留下大量内存(以阻止 GC 抖动)。但是,如果您反复捕获它,您显然会回到 GC 抖动。
  • 我认为不应该捕获未经检查的异常!
  • 我在我身边尝试过,使用 jdk7,但它确实捕获了 OutOfMemory 错误!
【解决方案5】:
try { for (;;); } finally { System.err.println("?"); }

在这种情况下,finally 将不会执行(除非已弃用的 Thread.stop 被调用,或者通过工具接口调用等价物)。

【讨论】:

  • 该页面声称抛出了 ThreadDeath 错误,并且在调用 Thread.stop() 时堆栈正常展开。有没有我错过的问题? download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/guide/…
  • 我不认为有什么问题。也许你正在想象一个不存在的捕获。如果我们输入一个显式的throw,那么finally 块将按预期执行。 try { throw new ThreadDeath(); } finally { System.err.println("?"); }
【解决方案6】:

此线程中错误地引用了 Sun 教程。

注意:如果在执行 try 或 catch 代码时 JVM 退出,那么 finally 块 不会执行。同样,如果执行 try 或 catch 代码的线程被中断或杀死,即使应用程序作为一个整体继续运行,finally 块也不会执行。

如果您仔细查看 sun 教程的 finally 块,它不会说“不会执行”而是“可能不会执行” 这是正确的描述

注意:如果在执行 try 或 catch 代码时 JVM 退出,则 finally 块可能不会执行。同样,如果执行 try 或 catch 代码的线程被中断或杀死,即使应用程序作为一个整体继续运行,finally 块可能也不会执行。

这种行为的明显原因是,对 system.exit() 的调用是在运行时系统线程中处理的,这可能需要一些时间来关闭 jvm,同时线程调度程序可以要求最终执行。所以 finally 被设计为始终执行,但如果你正在关闭 jvm,jvm 可能会在最终执行之前关闭。

【讨论】:

    【解决方案7】:

    如果try 块内发生死锁/活锁。

    下面是演示它的代码:

    public class DeadLocker {
        private static class SampleRunnable implements Runnable {
            private String threadId;
            private Object lock1;
            private Object lock2;
    
            public SampleRunnable(String threadId, Object lock1, Object lock2) {
                super();
                this.threadId = threadId;
                this.lock1 = lock1;
                this.lock2 = lock2;
            }
    
            @Override
            public void run() {
                try {
                    synchronized (lock1) {
                        System.out.println(threadId + " inside lock1");
                        Thread.sleep(1000);
                        synchronized (lock2) {
                            System.out.println(threadId + " inside lock2");
                        }
                    }
                } catch (Exception e) {
                } finally {
                    System.out.println("finally");
                }
            }
    
        }
    
        public static void main(String[] args) throws Exception {
            Object ob1 = new Object();
            Object ob2 = new Object();
            Thread t1 = new Thread(new SampleRunnable("t1", ob1, ob2));
            Thread t2 = new Thread(new SampleRunnable("t2", ob2, ob1));
            t1.start();
            t2.start();
        }
    }
    

    此代码产生以下输出:

    t1 inside lock1
    t2 inside lock1
    

    “终于”永远不会被打印出来

    【讨论】:

    • 从技术上讲,try 块永远不会退出,因此 finally 块永远不会有机会执行。无限循环也是如此。
    【解决方案8】:

    如果在执行 try 或 catch 代码时 JVM 退出,则 finally 块可能不会执行。 (source)

    正常关闭 - 这发生在最后一个非守护线程退出或 Runtime.exit() (source) 时

    当一个线程退出时,JVM 会清点正在运行的线程,如果剩下的唯一线程是守护线程,它会启动有序关闭。当 JVM 停止时,任何剩余的守护线程都被放弃,finally 块不执行,堆栈不展开,JVM 就退出。应该谨慎使用守护线程,很少有处理活动可以随时安全地放弃而无需清理。特别是,将守护线程用于可能执行任何类型 I/O 的任务是很危险的。守护线程最好保存用于“管家”任务,例如定期从内存缓存中删除过期条目的后台线程。 (source)

    最后一个非守护线程退出示例:

    public class TestDaemon {
        private static Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    while (true) {
                        System.out.println("Is alive");
                        Thread.sleep(10);
                        // throw new RuntimeException();
                    }
                } catch (Throwable t) {
                    t.printStackTrace();
                } finally {
                    System.out.println("This will never be executed.");
                }
            }
        };
    
        public static void main(String[] args) throws InterruptedException {
            Thread daemon = new Thread(runnable);
            daemon.setDaemon(true);
            daemon.start();
            Thread.sleep(100);
            // daemon.stop();
            System.out.println("Last non-daemon thread exits.");
        }
    }
    

    输出:

    Is alive
    Is alive
    Is alive
    Is alive
    Is alive
    Is alive
    Is alive
    Is alive
    Is alive
    Is alive
    Last non-daemon thread exits.
    Is alive
    Is alive
    Is alive
    Is alive
    Is alive
    

    【讨论】:

      【解决方案9】:

      在以下情况下,finally块将不会被执行:-

      • 当从try 块调用System.exit(0) 时。
      • 当JVM内存不足时
      • 当你的 java 进程被任务管理器或控制台强行杀死时
      • try 块中的死锁条件
      • 当您的机器因电源故障而关闭时

      可能还有其他一些边缘情况,finally块不会被执行。

      【讨论】:

        【解决方案10】:

        有两种方法可以停止 finally 块代码的执行:
        1. 使用 System.exit();
        2. 如果不知何故执行控制达不到尝试块。
        见:

        public class Main
        {
          public static void main (String[]args)
          {
            if(true){
                System.out.println("will exceute");
            }else{
                try{
                    System.out.println("result = "+5/0);
                }catch(ArithmeticException e){
                  System.out.println("will not exceute");
                }finally{
                  System.out.println("will not exceute");  
                }
            }
          }
        }
        

        【讨论】:

          【解决方案11】:

          我遇到了一个非常具体的情况,即 finally 块没有执行与播放框架特别相关。

          我惊讶地发现这个控制器动作代码中的 finally 块只在异常之后被调用,但在调用实际成功时从未被调用。

          try {
              InputStream is = getInputStreamMethod();
              renderBinary(is, "out.zip");
          catch (Exception e) {
              e.printStackTrace();
          } finally {
              cleanUp();
          }
          

          也许线程被终止或调用 renderBinary() 时发生了什么。我怀疑其他 render() 调用也会发生同样的事情,但我没有验证它。

          我通过将 renderBinary() 移到 try/catch 之后解决了这个问题。进一步的调查显示 play 提供了一个 @Finally 注释来创建一个在控制器操作执行后执行的方法。这里需要注意的是,这将在控制器中执行 ANY 操作后被调用,因此它可能并不总是一个好的选择。

          【讨论】:

            【解决方案12】:
            //If ArithmeticException Occur Inner finally would not be executed
            class Temp
            {
                public static void main(String[] s)
                {
                    try
                    {
                    int x = 10/s.length;
                    System.out.println(x);
                    try
                        {
                            int z[] = new int[s.length];
                            z[10] = 1000;
                        }catch(ArrayIndexOutOfBoundsException e)
                        {
                            System.out.println(e);
                        }
                     finally
                    {
                        System.out.println("Inner finally");
                    }
                    }
                    catch(ArithmeticException e)
                    {
                        System.out.println(e);
                    }
                finally 
                {
                    System.out.println("Outer Finally"); 
                }
            
            System.out.println("Remaining Code");   
            }
            }
            

            【讨论】:

            • 改进缩进并添加一些细节。
            • 执行连内部的try块都达不到,当然内部的finally也不会执行。
            猜你喜欢
            • 2011-02-18
            • 2012-05-02
            • 1970-01-01
            • 2017-09-04
            • 2019-06-11
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多