Finally 块总是运行吗?

有没有可能最终不能在 Java 中运行的情况? 谢谢。

84002 次浏览

System.exit shuts down the Virtual Machine.

终止当前运行的 Java Virtual Machine. The argument serves 作为一种状态代码; 按照惯例,一个 非零状态码显示异常 终止合约。

中调用 exit方法 类 Runtime。此方法从不 正常返回。

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

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

Sun 教程

注意: 如果在 try 时 JVM 退出 or catch code is being executed, then Finally 块可能无法执行。 类似地,如果执行 Try 或 catch 代码被中断或 被杀死,finally 块可能不会 即使应用程序作为 一切还在继续。

我不知道 finally 块还有什么不能执行的方法。

为了扩展其他人所说的内容,任何不会导致 JVM 退出的内容都将引发 finally 块。因此,下面的方法:

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

将奇怪地同时编译并返回1。

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

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

catch (OutOfMemoryError oome) {
// do stuff
}

这不起作用,因为 JVM 没有内存来执行 catch 块。

try { for (;;); } finally { System.err.println("?"); }

In that case the finally will not execute (unless the deprecated Thread.stop is called, or an equivalent, say, through a tools interface).

Sun 教程在这个帖子中被错误地引用了。

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

如果你仔细查看 sun 教程中的 finally block,它并没有说“ will not execute”,而是说“ may not execute” 这是正确的描述

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

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

同样,如果在 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();
}
}

This code produces the following output:

t1 inside lock1
t2 inside lock1

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

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

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

当一个线程退出时,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

我遇到过一个与 play 框架相关的 finally 块不能执行的非常特殊的情况。

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

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

当调用 renderBinary ()时,线程可能被终止或发生了其他情况。我怀疑同样的事情也会发生在其他的渲染()调用上,但是我没有验证它。

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

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

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

  • try块调用 System.exit(0)时。
  • 当 JVM 耗尽内存时
  • 当从任务 mgr 或控制台强制关闭 Java 进程时
  • try块中的死锁状态
  • When your machine shuts down due power failure

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

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

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