web-dev-qa-db-fra.com

Est-ce qu'un bloc enfin est toujours exécuté?

Y a-t-il une condition où finalement pourrait ne pas fonctionner en java? Merci.

110
Warrior

à partir du tutoriels Sun

Remarque: Si la machine virtuelle Java se ferme pendant l'exécution du code try ou catch, le bloc finally peut ne pas s'exécuter. De même, si le thread qui exécute le code try ou catch est interrompu ou tué, le bloc finally peut ne pas s'exécuter même si l'application continue dans son ensemble.

Je ne connais pas d'autre moyen d'empêcher le bloc final de s'exécuter ...

136
hhafez

System.exit arrête la machine virtuelle.

Termine la machine virtuelle en cours d'exécution Java. L'argument sert de code d'état; par convention, un code d'état différent de zéro indique une interruption anormale.

Cette méthode appelle la méthode exit de la classe Runtime. Cette méthode ne retourne jamais normalement.

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

"bye" ne s'affiche pas dans le code ci-dessus.

62
Eugene Yokota

Juste pour développer ce que d’autres ont dit, tout ce qui n’entraîne pas un événement similaire à celui de la sortie de la machine virtuelle Java entraîne le blocage du blocage. Donc, la méthode suivante:

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

étrangement à la fois compiler et retourner 1.

49
Daniel Nadasi

En relation avec System.exit, il existe également certains types d'échec catastrophique dans lesquels un bloc finally peut ne pas s'exécuter. Si la machine virtuelle Java manque complètement de mémoire, il est possible qu'elle se ferme sans incident ou finalement.

Plus précisément, je me souviens d’un projet où nous avons bêtement essayé d’utiliser

catch (OutOfMemoryError oome) {
    // do stuff
}

Cela n'a pas fonctionné car la machine virtuelle Java n'avait plus de mémoire pour exécuter le bloc catch.

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

Dans ce cas, la commande finally ne sera pas exécutée (à moins que le Thread.stop est appelé, ou un équivalent, par exemple, via une interface d’outils).

10

Le tutoriel Sun a été cité à tort ici dans ce fil de discussion.

Remarque: Si la machine virtuelle Java se ferme pendant l'exécution du code try ou catch, le bloc finally sera ne sera pas exécuté. De même, si le thread qui exécute le code try ou catch est interrompu ou tué, le bloc finally sera ne sera pas exécuté même si l'application dans son ensemble continue.

Si vous examinez attentivement le tutoriel Sun pour finalement bloquer, il ne dit pas "n'exécutera pas" mais "ne pourra pas s'exécuter" Voici la description correcte

Remarque: Si la machine virtuelle Java se ferme pendant l'exécution du code try ou catch, le bloc finally peut ne s'exécute pas. De même, si le thread exécutant le code try ou catch est interrompu ou tué, le bloc finally peut ne s'exécute pas, même si l'application dans son ensemble continue.

La raison apparente de ce comportement est que l’appel à system.exit () est traité dans un thread système d’exécution, ce qui peut prendre un certain temps pour arrêter jvm, tandis que le planificateur de thread peut demander à être exécuté. Donc, finalement, est conçu pour toujours être exécuté, mais si vous arrêtez jvm, il peut arriver que jvm s’arrête avant d’être enfin exécuté.

8
saurabh

Aussi, si un blocage/livelock se produit dans le bloc try.

Voici le code qui le démontre:

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

Ce code produit la sortie suivante:

t1 inside lock1
t2 inside lock1

et "enfin" n'est jamais imprimé

6
wheleph

Voici quelques conditions qui peuvent contourner un blocage final:

  1. Si la machine virtuelle Java se ferme pendant l'exécution du code try ou catch, le bloc finally peut ne pas s'exécuter.
  2. Arrêt normal - cela se produit lorsque le dernier thread non démon se ferme OR lorsque Runtime.exit () ()
  3. Lorsqu'un thread se ferme, la machine virtuelle Java effectue un inventaire des threads en cours d'exécution. Si les seuls threads restant sont des threads de démon, elle déclenche un arrêt méthodique. Lorsque la machine virtuelle Java s'arrête, tous les threads de démon restants sont abandonnés. Enfin, les blocs ne sont pas exécutés, les piles ne sont pas déroulées, la machine virtuelle Java vient de quitter. Les threads Daemon doivent être utilisés avec parcimonie. Peu d’activités de traitement peuvent être abandonnées en toute sécurité à tout moment sans nettoyage. En particulier, il est dangereux d’utiliser des threads de démon pour les tâches susceptibles d’exécuter toute sorte d’E/S. Les threads de démon sont mieux sauvegardés pour les tâches "de maintenance", tels qu'un thread d'arrière-plan qui supprime périodiquement les entrées expirées d'un cache en mémoire.

Dernier exemple de thread non démon:

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

Sortie:

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
5
Mykhaylo Adamovych

Il y a deux façons d'arrêter finalement de bloquer l'exécution du code:
1. Utilisez System.exit ();
2. Si, d'une manière ou d'une autre, le contrôle d'exécution ne parvient pas à essayer de bloquer.
Voir:

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

Dans les cas suivants, finalement, le blocage ne sera pas exécuté: -

  • Lorsque System.exit(0) est appelé à partir du bloc try.
  • Quand la JVM manque de mémoire
  • Lorsque votre processus Java est tué de force du gestionnaire de tâches ou de la console
  • Condition de blocage dans votre bloc try
  • Lorsque votre machine s'éteint en raison d'une panne de courant

Il peut également y avoir d'autres cas marginaux, où le blocage final ne sera pas exécuté.

Je suis tombé sur un cas très spécifique de blocage du blocage final lié spécifiquement à la structure de jeu.

J'ai été surpris de constater que le dernier bloc de ce code d'action de contrôleur n'a été appelé qu'après une exception, mais jamais lorsque l'appel a abouti.

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

Peut-être que le thread est terminé ou quelque chose quand renderBinary () est appelé. Je soupçonne que la même chose se produit pour les autres appels de render (), mais je ne l'ai pas vérifiée.

J'ai résolu le problème en déplaçant la renderBinary () après le try/catch. Une enquête plus approfondie a révélé que play fournissait une annotation @Finally pour créer une méthode qui est exécutée après l'exécution d'une action du contrôleur. La mise en garde ici est que cela sera appelé après l'exécution de N'IMPORTE QUELLE action dans le contrôleur, donc ce n'est peut-être pas toujours le bon choix.

0
Jeremy Goodell