Wann sollten wir System.exit in Java aufrufen?

193

Was ist in Java der Unterschied mit oder ohne System.exit(0)folgenden Code?

public class TestExit
{      
    public static void main(String[] args)
    { 
        System.out.println("hello world");

        System.exit(0);  // is it necessary? And when it must be called? 
    }      
}

Das Dokument sagt: "Diese Methode gibt niemals normal zurück." Was heißt das?

pierrotlefou
quelle

Antworten:

207

System.exit()kann verwendet werden, um Shutdown-Hooks auszuführen , bevor das Programm beendet wird. Dies ist eine bequeme Möglichkeit, das Herunterfahren in größeren Programmen zu handhaben, in denen sich alle Teile des Programms nicht bewusst sein können (und sollten). Wenn jemand beenden möchte, kann er einfach anrufen System.exit(), und die Shutdown-Hooks (sofern ordnungsgemäß eingerichtet) sorgen für alle erforderlichen Shutdown-Zeremonien wie das Schließen von Dateien, das Freigeben von Ressourcen usw.

"Diese Methode kehrt nie normal zurück." bedeutet nur, dass die Methode nicht zurückkehrt; Sobald ein Thread dorthin geht, kommt er nicht mehr zurück.

Eine andere, vielleicht häufigere Möglichkeit, ein Programm zu beenden, besteht darin, einfach das Ende der mainMethode zu erreichen . Wenn jedoch Nicht-Daemon-Threads ausgeführt werden, werden diese nicht heruntergefahren und die JVM wird daher nicht beendet. Wenn Sie also solche Nicht-Daemon-Threads haben, benötigen Sie andere Mittel (als die Shutdown-Hooks), um alle Nicht-Daemon-Threads herunterzufahren und andere Ressourcen freizugeben. Wenn keine anderen Nicht-Daemon-Threads vorhanden sind, wird durch die Rückkehr von maindie JVM heruntergefahren und die Shutdown-Hooks aufgerufen.

Aus irgendeinem Grund scheinen Shutdown-Hooks ein unterbewerteter und missverstandener Mechanismus zu sein, und die Leute erfinden das Rad mit allen Arten von proprietären benutzerdefinierten Hacks neu, um ihre Programme zu beenden. Ich würde die Verwendung von Abschalthaken empfehlen. Es ist alles in der Standard- Laufzeit vorhanden , die Sie sowieso verwenden werden.

Joonas Pulakka
quelle
10
"Diese Methode kehrt nie normal zurück." bedeutet nur, dass die Methode nicht zurückkehrt; Sobald ein Thread dorthin geht, kommt er nicht mehr zurück. Beachten Sie insbesondere, dass dies bedeutet, dass Sie keine Methode testen können, die einen System.exit (0) -Aufruf ausführt ...
Bill Michell
5
-1 Falsch. Shutdown-Hooks werden ausgeführt, wenn die JVM normal beendet wird, unabhängig davon, ob dies auf System.exit oder die Beendigung von main () zurückzuführen ist. Siehe zx81 / doku / java / javadoc / j2se1.5.0 / docs / api / java / lang /…
sleske
31
@sleske: Die Beendigung von main () reicht nicht aus, wenn andere Nicht-Daemon-Threads vorhanden sind. Das Herunterfahren wird erst nach dem Beenden des letzten Nicht-Daemon-Threads eingeleitet, es sei denn, Sie rufen System.exit () explizit auf. Dies ist in der Runtime-Dokumentation klar angegeben.
Joonas Pulakka
3
Beachten Sie, dass Sie einen Deadlock durchführen, wenn Ihr Shutdown-Hook wiederum auf dem Thread mit dem Namen System.exit basiert.
Djechlin
8
Etwas hinzuzufügen, wenn jemand eine Laufzeit anhält, werden Shutdown-Hooks nicht ausgeführt ( Runtime.getRuntime().halt())
Rogue
50

In diesem Fall wird es nicht benötigt. Es wurden keine zusätzlichen Threads gestartet, Sie ändern den Exit-Code nicht (standardmäßig 0) - im Grunde ist es sinnlos.

Wenn die Dokumente sagen, dass die Methode niemals normal zurückgegeben wird, bedeutet dies, dass die nachfolgende Codezeile praktisch nicht erreichbar ist, obwohl der Compiler das nicht weiß:

System.exit(0);
System.out.println("This line will never be reached");

Entweder wird eine Ausnahme ausgelöst oder die VM wird vor der Rückkehr beendet. Es wird niemals "einfach zurückkehren".

Es ist sehr selten wert, System.exit()IME anzurufen. Es kann sinnvoll sein, wenn Sie ein Befehlszeilentool schreiben und einen Fehler über den Exit-Code anzeigen möchten, anstatt nur eine Ausnahme auszulösen. Ich kann mich jedoch nicht erinnern, wann ich ihn das letzte Mal im normalen Produktionscode verwendet habe .

Jon Skeet
quelle
2
Warum weiß der Compiler das nicht? Ist System.exit () nicht speziell genug, um einen bestimmten Erkennungscode zu gewährleisten?
Bart van Heukelom
3
@ Bart: Nein, das glaube ich nicht. Das Einfügen von Sonderfällen in die Sprache für solche Dinge erhöht die Sprachkomplexität mit sehr geringem Nutzen.
Jon Skeet
Ich dachte, "kehrt nie normal zurück" hat mit "abruptem Abschluss" der Aussage zu tun. (JLS Abschnitt 14.1). Liege ich falsch?
Aioobe
@aioobe: Nein, du hast recht. System.exit()wird nie normal abgeschlossen - es wird immer entweder mit einer Ausnahme oder mit dem Herunterfahren der VM abgeschlossen (was im JLS nicht wirklich behandelt wird).
Jon Skeet
1
@piechuckerr: Warum denkst du das? Es ist völlig vernünftig, es mit einem anderen Wert als 0 aufzurufen, um der aufrufenden Shell (oder was auch immer) anzuzeigen, dass das Programm auf einen Fehler gestoßen ist.
Jon Skeet
15

Die Methode wird nie zurückgegeben, da dies das Ende der Welt ist und keiner Ihrer Codes als nächstes ausgeführt wird.

In Ihrem Beispiel wird Ihre Anwendung ohnehin an derselben Stelle im Code beendet, wenn Sie jedoch System.exit verwenden. Sie haben die Möglichkeit, einen benutzerdefinierten Code an die Umgebung zurückzugeben, z

System.exit(42);

Wer wird Ihren Exit-Code verwenden? Ein Skript, das die Anwendung aufruft. Funktioniert unter Windows, Unix und allen anderen skriptfähigen Umgebungen.

Warum einen Code zurückgeben? Um Dinge wie "Ich hatte keinen Erfolg" zu sagen, "Die Datenbank hat nicht geantwortet".

Um zu sehen, wie Sie den Wert eines Exit-Codes abrufen und in einem Unix-Shell-Skript oder einem Windows-Cmd-Skript verwenden können, überprüfen Sie diese Antwort möglicherweise auf dieser Site

mico
quelle
14

System.exit(0)beendet die JVM. In einfachen Beispielen wie diesem ist es schwierig, den Unterschied zu erkennen. Der Parameter wird an das Betriebssystem zurückgegeben und normalerweise verwendet, um auf eine abnormale Beendigung hinzuweisen (z. B. einen schwerwiegenden Fehler). Wenn Sie also Java aus einer Batchdatei oder einem Shell-Skript aufrufen, können Sie diesen Wert abrufen und sich ein Bild machen wenn die Bewerbung erfolgreich war.

Es würde erhebliche Auswirkungen haben, wenn Sie eine System.exit(0)auf einem Anwendungsserver bereitgestellte Anwendung aufrufen würden (denken Sie darüber nach, bevor Sie es versuchen).

Qwerky
quelle
11

In Anwendungen mit komplexen Shutdown-Hooks sollte diese Methode nicht von einem unbekannten Thread aufgerufen werden. System.exitWird nie normal beendet, da der Anruf blockiert wird, bis die JVM beendet wird. Es ist, als ob der Code ausgeführt wird, an dem der Netzstecker gezogen ist, bevor er beendet werden kann. Durch das Aufrufen System.exitwerden die Shutdown-Hooks des Programms ausgelöst, und der aufgerufene Thread System.exitwird bis zum Beenden des Programms blockiert. Dies hat zur Folge, dass System.exitdas Programm blockiert , wenn der Shutdown-Hook wiederum eine Aufgabe an den Thread sendet, von dem aus aufgerufen wurde.

Ich behandle dies in meinem Code wie folgt:

public static void exit(final int status) {
    new Thread("App-exit") {
        @Override
        public void run() {
            System.exit(status);
        }
    }.start();
}
Djechlin
quelle
Ich hatte auch das Problem, dass System.exit die JVM nicht wirklich beenden würde. Es geschah jedoch nur unter bestimmten Bedingungen. Ein Threadump, den ich genommen habe, gab mir keine Einblicke darüber, welche Threads / Shutdown-Handler das Herunterfahren blockieren. Die Verwendung des im Kommentar beschriebenen Codes hat mir geholfen, ihn zu lösen!
Kai
7

Die Antwort war zwar sehr hilfreich, aber einige haben einige zusätzliche Details übersehen. Ich hoffe, dass das Folgende zusätzlich zur obigen Antwort zum Verständnis des Herunterfahrens in Java beiträgt:

  1. Bei einem ordnungsgemäßen * Herunterfahren startet die JVM zuerst alle registrierten Shutdown-Hooks. Shutdown-Hooks sind nicht gestartete Threads, die bei Runtime.addShutdownHook registriert sind.
  2. JVM übernimmt keine Garantie für die Reihenfolge, in der die Shutdown-Hooks gestartet werden. Wenn zum Herunterfahren noch Anwendungsthreads (Daemon oder Nondaemon) ausgeführt werden, werden sie gleichzeitig mit dem Herunterfahren fortgesetzt .
  3. Wenn alle Shutdown-Hooks abgeschlossen sind, kann die JVM Finalizer ausführen, wenn runFinalizersOnExit true ist, und dann anhalten.
  4. JVM unternimmt keinen Versuch, Anwendungsthreads zu stoppen oder zu unterbrechen, die zum Zeitpunkt des Herunterfahrens noch ausgeführt werden. Sie werden abrupt beendet, wenn die JVM schließlich angehalten wird.
  5. Wenn die Shutdown-Hooks oder Finalizer nicht abgeschlossen sind, „hängt“ der ordnungsgemäße Shutdown-Prozess und die JVM muss abrupt heruntergefahren werden.
  6. Bei einem plötzlichen Herunterfahren muss die JVM nichts anderes tun, als die JVM anzuhalten. Shutdown-Hooks werden nicht ausgeführt.

PS: Die JVM kann ordnungsgemäß oder abrupt heruntergefahren werden.

  1. Ein ordnungsgemäßes Herunterfahren wird eingeleitet, wenn der letzte "normale" (Nondaemon) Thread beendet wird, jemand System.exit aufruft oder auf andere plattformspezifische Weise (z. B. durch Senden eines SIGINT oder Drücken von Strg-C).
  2. Während oben die Standard- und bevorzugte Methode zum Herunterfahren der JVM ist, kann sie auch abrupt heruntergefahren werden, indem Runtime.halt aufgerufen wird oder der JVM-Prozess über das Betriebssystem beendet wird (z. B. durch Senden eines SIGKILL).
John
quelle
7

Man sollte NIEMALS System.exit(0)aus folgenden Gründen anrufen :

  1. Es ist ein verstecktes "goto" und "gotos" unterbrechen den Kontrollfluss. Sich in diesem Zusammenhang auf Hooks zu verlassen, ist eine mentale Zuordnung, die jeder Entwickler im Team kennen muss.
  2. Das Beenden des Programms "normal" liefert dem Betriebssystem den gleichen Exit-Code, System.exit(0)so dass es redundant ist.

    Wenn Ihr Programm nicht "normal" beendet werden kann, haben Sie die Kontrolle über Ihre Entwicklung [Design] verloren. Sie sollten immer die volle Kontrolle über den Systemstatus haben.

  3. Programmierprobleme wie das Ausführen von Threads, die nicht gestoppt werden, werden normalerweise ausgeblendet.
  4. Möglicherweise tritt ein inkonsistenter Anwendungsstatus auf, der Threads abnormal unterbricht. (Siehe Nr. 3)

Übrigens: Die Rückgabe anderer Rückkehrcodes als 0 ist sinnvoll, wenn Sie auf eine abnormale Programmbeendigung hinweisen möchten.

oopexpert
quelle
2
"Sie sollten immer die volle Kontrolle über den Systemstatus haben." Das ist in Java einfach nicht möglich. IoC-Frameworks und Anwendungsserver sind nur zwei sehr beliebte und weit verbreitete Methoden, mit denen Sie die Kontrolle über den Systemstatus aufgeben. Und das ist völlig in Ordnung.
Mhlz
Bitte führen Sie System.exit (0) auf Ihrem Anwendungsserver aus und teilen Sie mir die Reaktionen Ihres Serveradministrators mit ... Sie haben das Thema dieser Frage und meine Antwort verpasst.
oopexpert
Vielleicht war ich nicht klar genug. Sie sollten immer die volle Kontrolle über den Systemstatus haben, für den Sie verantwortlich sind. Ja, einige Verantwortlichkeiten wurden auf Anwendungsserver und IoC verlagert. Aber darüber habe ich nicht gesprochen.
oopexpert
5

System.exit wird benötigt

  • wenn Sie einen Fehlercode ungleich 0 zurückgeben möchten
  • wenn Sie Ihr Programm von einem Ort beenden möchten, der nicht main () ist

In Ihrem Fall macht es genau das Gleiche wie die einfache Rückkehr von der Hauptleitung.

mfx
quelle
Was meinst du mit letzterem? Der richtige Weg, um das Programm herunterzufahren, besteht darin, alle Threads (nett) zu stoppen. Dies ist eine Bewegung, die nicht vom Haupt-Thread aus initiiert werden muss. Es ist also nicht so, exitals wäre dies Ihre einzige Option.
Bart van Heukelom
@Bart: Was Sie beschreiben, ist eine der möglichen Möglichkeiten , ein Programm herunterzufahren, nicht die richtige . Es ist nichts Falsches daran, Shutdown-Hooks zu verwenden, um alle Threads gut zu stoppen. Und Shutdown-Hooks werden mit gestartet exit. Nicht die einzige Option, aber definitiv eine erwägenswerte Option.
Joonas Pulakka
Aber was ich aus den Dokumenten entnehme, sind Shutdown-Hooks sehr heikel und haben möglicherweise nicht viel Zeit, um das zu tun, was Sie von ihnen wollen. Verwenden Sie sie auf jeden Fall, um sie im Falle eines Herunterfahrens des Betriebssystems oder ähnlichem zu beenden. Wenn Sie jedoch System.exit () selbst aufrufen möchten, ist es meiner Meinung nach besser, den Code zum Herunterfahren vorher auszuführen (danach werden Sie es wahrscheinlich nicht mehr tun) brauche System.exit () nicht mehr)
Bart van Heukelom
1
Shutdown-Haken haben die ganze Zeit, die sie brauchen, um fertig zu werden. Die VM wird nicht angehalten, bevor alle Hooks zurückgekehrt sind. Es ist in der Tat möglich, das Beenden der VM zu verhindern, indem ein Shutdown-Hook registriert wird, dessen Ausführung sehr lange dauert. Aber natürlich ist es möglich, die Abschaltsequenz auf verschiedene, alternative Arten durchzuführen.
Joonas Pulakka
Die Rückkehr von main wird nicht beendet, wenn andere Nicht-Daemon-Threads vorhanden sind.
Djechlin
4

Java Language Specification sagt das

Programm beenden

Ein Programm beendet alle seine Aktivitäten und wird beendet, wenn eines von zwei Dingen passiert:

Alle Threads, die keine Daemon-Threads sind, werden beendet.

Einige Threads rufen die Exit-Methode der Klasse Runtime oder der Klasse System auf , und der Exit-Vorgang wird vom Sicherheitsmanager nicht verboten.

Es bedeutet, dass Sie es verwenden sollten, wenn Sie ein großes Programm haben (zumindest größer als dieses) und dessen Ausführung beenden möchten.

Marcin Szymczak
quelle
3

Wenn in der JVM ein anderes Programm ausgeführt wird und Sie System.exit verwenden, wird auch dieses zweite Programm geschlossen. Stellen Sie sich zum Beispiel vor, Sie führen einen Java-Job auf einem Clusterknoten aus und das Java-Programm, das den Clusterknoten verwaltet, wird in derselben JVM ausgeführt. Wenn der Job System.exit verwenden würde, würde er nicht nur den Job beenden, sondern auch "den gesamten Knoten herunterfahren". Sie können keinen weiteren Job an diesen Clusterknoten senden, da das Verwaltungsprogramm versehentlich geschlossen wurde.

Verwenden Sie daher System.exit nicht, wenn Sie Ihr Programm von einem anderen Java-Programm innerhalb derselben JVM aus steuern möchten.

Verwenden Sie System.exit, wenn Sie die gesamte JVM absichtlich schließen möchten und die in den anderen Antworten beschriebenen Möglichkeiten nutzen möchten (z. B. Hooks zum Herunterfahren : Java-Shutdown-Hook , Rückgabewert ungleich Null für die Befehlszeile Aufrufe: So erhalten Sie den Exit-Status eines Java-Programms in einer Windows-Batchdatei ).

Schauen Sie sich auch Runtime Exceptions: System.exit (num) an oder werfen Sie eine RuntimeException von main?

Stefan
quelle