¿Un bloque finalmente se ejecuta siempre?


112

¿Existe alguna condición en la que finalmente no se pueda ejecutar en Java? Gracias.


13
¿Es esta una pregunta que le pueden hacer al intentar conseguir un trabajo en una determinada empresa conocida?
Tom Hawtin - tackline

@ TomHawtin-tackline ¿Te importaría nombrarlo? (¡Dios, extrañé la antigüedad de esta publicación!)
Hele

6
@Hele, no me gustaría regalar el juego, pero puedes buscarlo en Google.
Tom Hawtin - tackline

respuesta corta: sí, en condiciones normales.
Tiburón

Respuestas:


139

de los tutoriales del sol

Nota: Si la JVM se cierra mientras se está ejecutando el código try o catch, es posible que el bloque finalmente no se ejecute. Asimismo, si el hilo que ejecuta el código try o catch se interrumpe o se mata, es posible que el bloque finalmente no se ejecute aunque la aplicación en su conjunto continúe.

No conozco otras formas en que el bloque finalmente no se ejecute ...


6
@dhiller - Estoy bastante seguro de que "apagar" está incluido en "Si la JVM sale ..." :-p
Jason Coco

2
@Jason Coco: Terminar (como en caso de pérdida de energía) no es lo mismo que salir; el segundo es un proceso más o menos organizado que culmina en el primero. ; p
user359996

2
AFAIK, si se interrumpe un hilo, no se detiene inmediatamente. Depende del código en el hilo detectar la interrupción y detener su tarea, por lo que finalmente el código debería ejecutarse.
Bart van Heukelom

2
Supongo que si se lanza una excepción en el bloque finalmente, el resto del bloque no se ejecuta.
Adriaan Koster

bueno eso apesta!
Eiran el

63

System.exit apaga la máquina virtual.

Termina la máquina virtual Java en ejecución. El argumento sirve como código de estado; por convención, un código de estado distinto de cero indica una terminación anormal.

Este método llama al exitmétodo en clase Runtime. Este método nunca regresa normalmente.

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

"bye" no se imprime en el código anterior.


Además, una excepción debería apagar la máquina virtual Java.
kaissun

3
Si ocurre una excepción mientras se ejecuta System.exit (0), finalmente se ejecutará el bloque.
Halil el

50

Solo para ampliar lo que otros han dicho, cualquier cosa que no cause algo como la salida de JVM incurrirá en el bloqueo final. Entonces el siguiente método:

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

extrañamente compilará y devolverá 1.


2
esto realmente me confundió por un par de horas hace unas semanas.
nickf

3
Se considera una mala idea devolver un valor de un bloque finalmente. Regrese solo desde el bloque try o regrese desde fuera del bloque try /. La mayoría de los IDE marcarán esto con una advertencia.
Ran Biron

1
@nickf Supongo que ya no estás confundido. ¿Podría explicar la mecánica de por qué se devuelve 1 y no 0? Solo puedo adivinar que la memoria (o es un registro) que almacena el valor de retorno de la función que inicialmente contiene 0, se sobrescribe cuando se ejecuta el bloque finalmente .
Yaneeve

2
Eso es curioso, en C # no se permite regresar de un bloque finalmente.
JMCF125

3
@RanBiron Por supuesto. En realidad, no estaba recomendando regresar dentro de un bloque final, solo estaba tratando de demostrar que incluso una declaración de retorno aún hará que se ejecute el código en dicho bloque.
Aquarelle

15

En relación con System.exit, también hay ciertos tipos de fallas catastróficas en las que un bloque finalmente puede no ejecutarse. Si la JVM se queda sin memoria por completo, es posible que se cierre sin que se detecte o que finalmente suceda.

Específicamente, recuerdo un proyecto en el que intentamos tontamente usar

catch (OutOfMemoryError oome) {
    // do stuff
}

Esto no funcionó porque a la JVM no le quedaba memoria para ejecutar el bloque catch.


Cuando se lanza OutOfMemoryError, generalmente queda mucha memoria (para detener la agitación de GC). Sin embargo, si lo atrapa repetidamente, obviamente volverá a la goleada de GC.
Tom Hawtin - tackline

¡Pensé que no se deberían detectar excepciones no controladas!
Sergii Shevchyk

1
Lo intenté en mi lado, usando jdk7, ¡pero detectó el error OutOfMemory!
Jaskey

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

En ese caso, el finalmente no se ejecutará (a menos que Thread.stopse llame al obsoleto , o un equivalente, digamos, a través de una interfaz de herramientas).


Esta página afirma que se produce un error de ThreadDeath y que la pila se desenrolla normalmente cuando se llama a Thread.stop (). ¿Hay una trampa que me falta? download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/guide/…
spurserh

No creo que haya trampa. Quizás ahí te estás imaginando una trampa que no está ahí. Si ponemos un explícito throw, entonces el finallybloque se ejecutará como se esperaba. try { throw new ThreadDeath(); } finally { System.err.println("?"); }
Tom Hawtin - tackline

9

El tutorial de Sun se ha citado incorrectamente aquí en este hilo.

Nota: Si las salidas de JVM mientras se está ejecutando el código try o catch, el bloque finally se ha ejecutado. Del mismo modo, si el hilo de ejecutar el código try o captura se interrumpe o se mató, el bloque finally se no se ejecutará aunque la aplicación en su conjunto sigue.

Si observa detenidamente el tutorial de Sun para finalmente bloquear, no dice "no se ejecutará" pero "puede que no se ejecute" Aquí está la descripción correcta

Nota: Si las salidas de JVM mientras se está ejecutando el código try o catch, el bloque finally pueden no ejecutarse. Del mismo modo, si el hilo de ejecutar el código try o captura se interrumpe o se mató, el bloque finally puede no ejecutarse aunque la aplicación en su conjunto sigue.

La razón aparente de este comportamiento es que la llamada a system.exit () se procesa en un subproceso del sistema en tiempo de ejecución que puede tardar un tiempo en cerrar el jvm, mientras que el programador del subproceso puede solicitar finalmente que se ejecute. Finalmente, está diseñado para ejecutarse siempre, pero si está cerrando jvm, puede suceder que jvm se apague antes de que finalmente se ejecute.


6

Además, si ocurre un interbloqueo / bloqueo activo dentro del trybloque.

Aquí está el código que lo demuestra:

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

Este código produce la siguiente salida:

t1 inside lock1
t2 inside lock1

y "finalmente" nunca se imprime


3
Técnicamente, el bloque try nunca sale, por lo que el bloque finalmente nunca debería tener la oportunidad de ejecutarse. Lo mismo podría decirse de un bucle infinito.
Jeff Mercado

6

Si la JVM se cierra mientras se está ejecutando el código try o catch, es posible que el bloque finalmente no se ejecute. ( fuente )

Apagado normal: esto ocurre cuando el último hilo que no es un demonio sale O cuando Runtime.exit () ( fuente )

Cuando un subproceso sale, la JVM realiza un inventario de los subprocesos en ejecución, y si los únicos subprocesos que quedan son subprocesos de demonio, inicia un cierre ordenado. Cuando la JVM se detiene, todos los subprocesos de demonio restantes se abandonan, finalmente los bloques no se ejecutan, las pilas no se desenrollan y la JVM simplemente sale. Los subprocesos de daemon deben usarse con moderación, pocas actividades de procesamiento pueden abandonarse de manera segura en cualquier momento sin limpieza. En particular, es peligroso utilizar subprocesos de demonio para tareas que podrían realizar cualquier tipo de E / S. Los subprocesos de Daemon se guardan mejor para tareas de "limpieza", como un subproceso en segundo plano que elimina periódicamente las entradas caducadas de un caché en memoria. ( fuente )

Ejemplo de salida del último hilo no demonio:

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

Salida:

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

En los siguientes casos, el bloque finalmente no se ejecutará: -

  • Cuando System.exit(0)se invoca desde tryblock.
  • Cuando JVM se queda sin memoria
  • Cuando su proceso de java se mata a la fuerza desde el administrador de tareas o la consola
  • Condición de interbloqueo en su trybloque
  • Cuando su máquina se apaga debido a un corte de energía

También puede haber otros casos marginales, donde finalmente no se ejecutará el bloqueo.


1

Hay dos formas de detener finalmente la ejecución del código de bloqueo:
1. Utilice System.exit ();
2. Si de alguna manera el control de ejecución no llega a intentar bloquear.
Ver:

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

Me he encontrado con un caso muy específico del bloque finalmente que no se ejecuta relacionado específicamente con el marco de juego.

Me sorprendió descubrir que el bloque finalmente en este código de acción del controlador solo se llamó después de una excepción, pero nunca cuando la llamada realmente tuvo éxito.

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

Quizás el hilo se termina o algo cuando se llama a renderBinary (). Sospecho que sucede lo mismo con otras llamadas a render (), pero no lo verifiqué.

Resolví el problema moviendo renderBinary () después de try / catch. Una investigación adicional reveló que play proporciona una anotación @Finally para crear un método que se ejecuta después de que se ejecuta una acción del controlador. La advertencia aquí es que se llamará después de la ejecución de CUALQUIER acción en el controlador, por lo que puede que no siempre sea una buena opción.


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

Mejore la sangría y agregue algunos detalles.
RUMANIA_engineer

1
La ejecución ni siquiera llegaría al bloque try interno, por supuesto, el interno finalmente no se ejecutaría.
Anton Arhipov
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.