Un blocage final est-il toujours exécuté?


112

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


13
Est-ce une question que l'on pourrait vous poser en essayant d'obtenir un emploi dans une certaine entreprise bien connue?
Tom Hawtin - tackline

@ TomHawtin-tackline Voulez-vous le nommer? (Dieu, j'ai manqué l'âge de ce message!)
Hele

6
@Hele Je ne voudrais pas donner le jeu, mais vous pouvez le rechercher sur Google.
Tom Hawtin - tackline

réponse courte: oui, dans des conditions normales.
Shark le

Réponses:


139

à partir des didacticiels 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 exécutant le code try ou catch est interrompu ou tué, le bloc finally peut ne pas s'exécuter même si l'application dans son ensemble continue.

Je ne connais aucune autre manière dont le bloc finally ne s'exécuterait pas ...


6
@dhiller - Je suis quasiment sûr que "power down" est inclus dans "If the JVM exits ..." :-p
Jason Coco

2
@Jason Coco: Mettre fin (comme en cas de perte d'alimentation) n'est pas tout à fait la même chose que de quitter; le second est un processus plus ou moins organisé aboutissant au premier. ; p
user359996

2
AFAIK, si un thread est interrompu, il n'est pas immédiatement arrêté. C'est au code dans le thread de détecter l'interruption et d'arrêter sa tâche, donc finalement le code doit s'exécuter.
Bart van Heukelom

2
Je suppose que si une exception est lancée dans le bloc finally, le reste du bloc n'est pas exécuté.
Adriaan Koster

bien ça craint!
eiran

63

System.exit arrête la machine virtuelle.

Met fin à la machine virtuelle Java en cours d'exécution. 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 exitméthode en 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'imprime pas dans le code ci-dessus.


Une exception doit également arrêter la machine virtuelle Java.
kaissun

3
Si une exception se produit lors de l'exécution de System.exit (0), alors le bloc finally s'exécutera.
halil

50

Juste pour développer ce que d'autres ont dit, tout ce qui ne provoque pas la sortie de la JVM entraînera le blocage final. Donc, la méthode suivante:

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

va étrangement compiler et retourner 1.


2
cela m'a vraiment dérouté pendant quelques bonnes heures il y a quelques semaines.
nickf le

3
Il est considéré comme une mauvaise idée de renvoyer une valeur à partir d'un bloc finally. Soit revenir uniquement du bloc try, soit revenir de l'extérieur du bloc try / finally. La plupart des IDE le marqueront avec un avertissement.
Ran Biron le

1
@nickf Je suppose que vous n'êtes plus confus. Pourriez-vous expliquer pourquoi 1 est retourné et non 0. Je ne pourrais que deviner que la mémoire (ou est-ce un registre) qui stocke la valeur de retour de la fonction qui contient initialement 0, est écrasée lorsque le bloc final est exécuté .
Yaneeve

2
C'est curieux, en C #, il n'est pas permis de revenir d'un bloc finally.
JMCF125

3
@RanBiron Bien sûr. Il ne recommandait pas réellement de revenir à l'intérieur d'un bloc finally, il essayait seulement de démontrer que même une instruction return provoquera toujours l'exécution du code dans ledit bloc.
Aquarelle

15

Lié à System.exit, il existe également certains types d'échec catastrophique où un bloc finally peut ne pas s'exécuter. Si la machine virtuelle Java est complètement à court de mémoire, elle peut simplement se fermer sans interruption 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.


Lorsque OutOfMemoryError est levé, il reste généralement beaucoup de mémoire (pour arrêter le battement GC). Cependant, si vous l'attrapez à plusieurs reprises, vous reviendrez évidemment à la raclée GC.
Tom Hawtin - tackline

Je pensais qu'il ne fallait pas attraper d'exceptions non contrôlées!
Sergii Shevchyk

1
J'ai essayé de mon côté, en utilisant jdk7, mais il a attrapé l'erreur OutOfMemory!
Jaskey

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

Dans ce cas, le finally ne s'exécutera pas (à moins que le déprécié ne Thread.stopsoit appelé, ou un équivalent, par exemple, via une interface d'outils).


Cette page prétend qu'une erreur ThreadDeath est levée et que la pile se déroule normalement lorsque Thread.stop () est appelé. Y a-t-il un piège qui me manque? download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/guide/…
spurserh

Je ne pense pas qu'il y ait un piège. Peut-être que là vous imaginez une prise qui n'est pas là. Si nous mettons un explicite throw, le finallybloc s'exécutera comme prévu. try { throw new ThreadDeath(); } finally { System.err.println("?"); }
Tom Hawtin - tackline

9

Le tutoriel Sun a été mal cité ici dans ce fil.

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

Si vous regardez de près le tutoriel de sun pour finalement bloquer, il ne dit pas "ne s'exécutera pas" mais "ne s'exécutera peut-être pas".

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 exécutant le code try ou catch est interrompu ou tué, le bloc finally peut ne pas s'exécuter 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 du système d'exécution, ce qui peut prendre du temps pour arrêter le jvm, tandis que le planificateur de thread peut demander enfin à s'exécuter. Donc, finalement, est conçu pour toujours s'exécuter, mais si vous arrêtez jvm, il peut arriver que jvm s'arrête avant d'être finalement exécuté.


6

Aussi si un blocage / blocage de la vie se produit à l'intérieur du trybloc.

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é


3
Techniquement, le bloc try ne se termine jamais, donc le bloc finally ne devrait jamais avoir une chance de s'exécuter. La même chose pourrait être dite pour une boucle infinie.
Jeff Mercado

6

Si la JVM se ferme pendant l'exécution du code try ou catch, le bloc finally peut ne pas s'exécuter. ( source )

Arrêt normal - cela se produit soit lorsque le dernier thread non démon se termine, soit lorsque Runtime.exit () ( source )

Lorsqu'un thread se termine, la JVM effectue un inventaire des threads en cours d'exécution, et si les seuls threads qui restent sont des threads démons, il lance un arrêt ordonné. Lorsque la JVM 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 JVM se ferme simplement. Les threads de démon 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 des tâches qui pourraient effectuer n'importe quel type d'E / S. Les threads de démon sont mieux enregistrés pour les tâches de «ménage», comme un thread d'arrière-plan qui supprime périodiquement les entrées expirées d'un cache en mémoire. ( source )

Exemple de sortie du dernier 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.");
    }
}

Production:

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

Dans les cas suivants, le bloc finally ne sera pas exécuté: -

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

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


1

Il y a deux façons d'arrêter enfin l'exécution du code de blocage:
1. Utilisez System.exit ();
2. Si en quelque sorte le contrôle d'exécution n'atteint pas pour 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");  
        }
    }
  }
}

0

Je suis tombé sur un cas très spécifique du bloc finally ne s'exécutant pas spécifiquement lié au cadre de jeu.

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

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 lorsque renderBinary () est appelé. Je soupçonnerais que la même chose se produit pour les autres appels render (), mais je ne l'ai pas vérifié.

J'ai résolu le problème en déplaçant le renderBinary () après le try / catch. Une enquête plus approfondie a révélé que play fournit une annotation @Finally pour créer une méthode qui est exécutée après l'exécution d'une action de 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 pas toujours un bon choix.


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

Améliorez l'indentation et ajoutez quelques détails.
ROMANIA_engineer

1
L'exécution n'atteindrait même pas le bloc try interne, bien sûr, le bloc interne ne serait finalement pas exécuté.
Anton Arhipov
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.