最终块是否总是运行?


112

有什么条件最终可能无法在Java中运行?谢谢。


13
在某家知名公司工作时可能会问这个问题吗?
Tom Hawtin-大头钉

@ TomHawtin-tackline介意吗?(上帝,我错过了这篇文章的年龄!)
Hele

6
@Hele我不想放弃游戏,但是你可以用谷歌搜索它。
Tom Hawtin-大头钉

简短答案:是,在正常情况下。
鲨鱼

Answers:


139

太阳教程

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

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


6
@dhiller-我很确定“如果JVM退出...”中包括“掉电”:-p
Jason Coco,2009年

2
@Jason Coco:终止(因为断电)与退出并不完全相同;后者是一个或多或少的组织过程,最终形成前者。; p
user359996

2
AFAIK,如果线程被中断,则不会立即暂停。取决于线程中的代码来检测中断并停止其任务,因此最终应该运行代码。
Bart van Heukelom

2
我猜想,如果在finally块中引发了异常,则该块的其余部分将不会执行。
Adriaan Koster

好糟糕!
eiran

63

System.exit关闭虚拟机。

终止当前正在运行的Java虚拟机。参数用作状态码;按照惯例,非零状态代码表示异常终止。

该方法调用exitclass中的方法Runtime。此方法永远不会正常返回。

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

“再见”不会在上述代码中打印出来。


同样,异常应关闭Java虚拟机。
kaissun 2012年

3
如果在执行System.exit(0)时发生异常,则将最终执行代码块。
halil 2015年

50

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

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

将奇怪地编译并返回1。


2
几个星期前,这让我困惑了好几个小时。
尼克

3
从finally块返回值被认为是一个坏主意。仅从try块返回,或者从try / finally块外部返回。大多数IDE都会用警告标记它。
Ran Biron

1
@nickf我知道你不再困惑了。您能否详细说明为什么返回1而不返回0的机制。我只能猜测,存储了最初保留0的函数的返回值的内存(或它是一个寄存器)会在执行finally块时被覆盖。
Yaneeve 2012年

2
很好奇,在C#中,不允许从finally块返回。
JMCF125 2013年

3
@RanBiron当然。他实际上并不建议在finally块内返回,而只是在试图证明即使return语句仍会导致所述块中的代码执行。
水彩画

15

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

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

catch (OutOfMemoryError oome) {
    // do stuff
}

这没有用,因为JVM没有剩余的内存来执行catch块。


当抛出OutOfMemoryError时,通常会剩下很多内存(以停止GC崩溃)。但是,如果您反复捕获它,显然您将回到GC崩溃状态。
Tom Hawtin-大头钉

我认为一个人不应该捕获未经检查的异常!
Sergii Shevchyk

1
我使用jdk7在我的身边尝试过,但确实捕获了OutOfMemory错误!
Jaskey 2014年

10
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/...
spurserh

我认为没有收获。也许您在想像那里没有的渔获物。如果我们放一个显式throw,则该finally块将按预期执行。try { throw new ThreadDeath(); } finally { System.err.println("?"); }
汤姆·哈汀

9

该线程在此处错误地引用了Sun教程。

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

如果您仔细查看sun教程中的“ finally”块,它不会说“将不执行”,而是“可能不执行”

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

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


6

另外,如果在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

并且“最终”永远不会被打印


3
从技术上讲,try块永远不会退出,因此finally块永远不会有执行的机会。对于无限循环也可以这样说。
Jeff Mercado 2012年

6

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

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

当线程退出时,JVM将对正在运行的线程进行清点,并且如果仅剩下的线程是守护程序线程,它将启动有序关闭。当JVM暂停时,所有剩余的守护程序线程都将被放弃,最后,块将不会执行,堆栈也不会解开,JVM只会退出。应当尽可能少地使用守护程序线程,很少有处理活动可以在没有清理的情况下随时安全地放弃。特别是,将守护程序线程用于可能执行任何类型的I / O的任务是危险的。最好将守护程序线程保存起来以用于“整理”任务,例如后台线程会定期从内存缓存中删除过期的条目。(来源

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

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

1

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

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

可能还有其他附带情况,其中不会执行finally块。


1

有两种方法可以停止最终阻止代码执行:
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");  
        }
    }
  }
}

0

我遇到了一个非常特殊的情况,即finally块未执行,这与播放框架有关。

我很惊讶地发现,该控制器操作代码中的finally块仅在Exception之后才被调用,而在调用实际上没有成功时才被调用。

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

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

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


-1
//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");   
}
}

改进缩进并添加一些细节。
ROMANIA_engineer

1
该执行甚至都不会到达内部try块,当然最终不会执行内部。
安东·阿希普夫
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.