Quando dovremmo chiamare System.exit in Java


193

In Java, qual è la differenza con o senza System.exit(0)nel seguente codice?

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? 
    }      
}

Il documento dice: "Questo metodo non ritorna mai normalmente." Cosa significa?

Risposte:


207

System.exit()può essere utilizzato per eseguire hook di arresto prima della chiusura del programma. Questo è un modo conveniente per gestire l'arresto in programmi più grandi, in cui tutte le parti del programma non possono (e non dovrebbero) essere reciprocamente consapevoli. Quindi, se qualcuno vuole uscire, può semplicemente chiamare System.exit()e gli hook di arresto (se impostati correttamente) si occupano di fare tutte le cerimonie di spegnimento necessarie come la chiusura dei file, il rilascio di risorse ecc.

"Questo metodo non ritorna mai normalmente." significa solo che il metodo non tornerà; una volta che un thread va lì, non ritorna.

Un altro, forse più comune, modo per uscire da un programma è semplicemente quello di raggiungere la fine del mainmetodo. Ma se ci sono thread non daemon in esecuzione, non verranno chiusi e quindi la JVM non verrà chiusa. Pertanto, se si dispone di tali thread non daemon, sono necessari altri mezzi (oltre agli hook di arresto) per chiudere tutti i thread non daemon e rilasciare altre risorse. Se non ci sono altri thread non daemon, il ritorno da mainarresta la JVM e chiamerà gli hook di arresto.

Per qualche motivo, gli hook di chiusura sembrano essere un meccanismo sottovalutato e incompreso, e le persone stanno reinventando la ruota con tutti i tipi di hack personalizzati per uscire dai loro programmi. Vorrei incoraggiare l'uso di hook di arresto; è tutto lì nel Runtime standard che utilizzerai comunque.


10
"Questo metodo non ritorna mai normalmente." significa solo che il metodo non tornerà; una volta che un thread va lì, non ritorna. Nota in particolare che questo significa che non puoi testare l'unità di un metodo che effettua una chiamata System.exit (0) ...
Bill Michell,

5
-1 errato. Gli hook di spegnimento verranno eseguiti se JVM termina normalmente, indipendentemente dal fatto che sia a causa di System.exit o della terminazione di main (). Vedi zx81 / doku / java / javadoc / j2se1.5.0 / docs / api / java / lang /…
sleske,

31
@sleske: la terminazione di main () non è sufficiente se ci sono altri thread non daemon in giro. L'arresto viene avviato solo dopo la fine dell'ultimo thread non daemon, a meno che non si chiami esplicitamente System.exit (). Questo è chiaramente indicato nella documentazione di Runtime.
Joonas Pulakka,

3
Nota che se il tuo hook di arresto a sua volta si basa sul thread che ha chiamato System.exit, ti bloccherai.
Djechlin,

8
Qualcosa da aggiungere, se qualcuno interrompe un runtime, gli hook di spegnimento non verranno eseguiti ( Runtime.getRuntime().halt())
Rogue,

50

In tal caso, non è necessario. Non saranno stati avviati thread aggiuntivi, non stai modificando il codice di uscita (il cui valore predefinito è 0) - in pratica è inutile.

Quando i documenti dicono che il metodo non ritorna mai normalmente, significa che la successiva riga di codice è effettivamente irraggiungibile, anche se il compilatore non sa che:

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

Verrà generata un'eccezione o la macchina virtuale verrà chiusa prima di tornare. Non "tornerà mai più".

È molto raro che valga la pena chiamare l' System.exit()IME. Può avere senso se stai scrivendo uno strumento da riga di comando e vuoi indicare un errore tramite il codice di uscita anziché generare un'eccezione ... ma non ricordo l'ultima volta che l'ho usato nel normale codice di produzione .


2
Perché il compilatore non lo sa? System.exit () non è abbastanza speciale da giustificare un codice di rilevamento specifico?
Bart van Heukelom,

3
@Bart: No, io non la penso così. Mettere casi speciali nella lingua per cose come questa aumenta la complessità della lingua con pochissimi benefici.
Jon Skeet,

Ho pensato che "non ritorna mai normalmente" aveva a che fare con il "completamento improvviso" dell'affermazione. (JLS sezione 14.1). Ho sbagliato?
aioobe,

@aioobe: No, hai ragione. System.exit()non verrà mai completato normalmente: verrà sempre completato con un'eccezione o con l'arresto della macchina virtuale (che non è realmente coperto in JLS).
Jon Skeet,

1
@piechuckerr: Cosa ti fa pensare? È del tutto ragionevole chiamarlo con un valore diverso da 0, per indicare alla shell chiamante (o qualsiasi altra cosa) che il programma ha riscontrato un errore.
Jon Skeet,

15

System.exit(0)termina la JVM. In semplici esempi come questo è difficile percepire la differenza. Il parametro viene restituito al sistema operativo e viene normalmente utilizzato per indicare una chiusura anomala (ad es. Un qualche tipo di errore fatale), quindi se si chiamava java da un file batch o da uno script di shell si sarebbe in grado di ottenere questo valore e farsi un'idea se l'applicazione ha avuto successo.

Avrebbe un grande impatto se si chiamasse System.exit(0)un'applicazione distribuita su un server delle applicazioni (pensarci prima di provarlo).


15

Il metodo non ritorna mai perché è la fine del mondo e nessuno del tuo codice verrà eseguito successivamente.

La tua applicazione, nel tuo esempio, uscirà comunque nello stesso punto del codice, ma se usi System.exit. hai la possibilità di restituire un codice personalizzato all'ambiente, come, diciamo

System.exit(42);

Chi utilizzerà il tuo codice di uscita? Uno script che ha chiamato l'applicazione. Funziona in Windows, Unix e tutti gli altri ambienti scriptabili.

Perché restituire un codice? Per dire cose come "Non sono riuscito", "Il database non ha risposto".

Per vedere come ottenere il valore di un codice di uscita e utilizzarlo in uno script shell unix o in uno script cmd di Windows, è possibile controllare questa risposta su questo sito


12

Nelle applicazioni che possono avere hook di arresto complessi, questo metodo non deve essere chiamato da un thread sconosciuto. System.exitnon esce mai normalmente perché la chiamata si bloccherà fino al termine della JVM. È come se qualunque codice fosse in esecuzione che ha la spina di alimentazione inserita prima che possa finire. La chiamata System.exitavvierà gli hook di arresto del programma e qualsiasi thread che chiama System.exitbloccherà fino al termine del programma. Ciò implica che se l'hook di arresto a sua volta sottopone un'attività al thread da cui è System.exitstato chiamato, il programma si bloccherà.

Sto gestendo questo nel mio codice con il seguente:

public static void exit(final int status) {
    new Thread("App-exit") {
        @Override
        public void run() {
            System.exit(status);
        }
    }.start();
}

Ho anche avuto il problema per cui System.exit non avrebbe effettivamente posto fine alla JVM. Tuttavia, è successo solo a determinate condizioni. Un threaddown che ho preso non mi ha dato alcuna idea su quali thread / gestori di shutdown stanno bloccando lo shutdown. L'utilizzo del codice descritto nel commento mi ha aiutato a risolverlo!
Kai,

7

Anche se la risposta è stata davvero utile, ma alcuni hanno perso qualche dettaglio in più. Spero di seguito che aiuti a capire il processo di spegnimento in Java, oltre alla risposta sopra:

  1. In un arresto ordinato *, JVM avvia innanzitutto tutti gli hook di arresto registrati. I hook di arresto sono thread non avviati registrati con Runtime.addShutdownHook.
  2. JVM non fornisce alcuna garanzia sull'ordine in cui vengono avviati gli hook di arresto. Se eventuali thread dell'applicazione (daemon o nondaemon) sono ancora in esecuzione al momento dell'arresto, continuano a essere eseguiti contemporaneamente al processo di arresto .
  3. Quando tutti gli hook di arresto sono stati completati, JVM può scegliere di eseguire i finalizzatori se runFinalizersOnExit è true, quindi si arresta.
  4. JVM non tenta di arrestare o interrompere i thread dell'applicazione che sono ancora in esecuzione al momento dell'arresto; vengono bruscamente interrotti quando alla fine JVM si ferma.
  5. Se i ganci di arresto o i finalizzatori non vengono completati, il processo di arresto ordinato "si blocca" e JVM deve essere arrestato bruscamente.
  6. In un arresto improvviso, la JVM non deve fare altro che arrestare la JVM; i ganci di arresto non verranno eseguiti.

PS: JVM può spegnersi in modo ordinato o brusco .

  1. Un arresto ordinato viene avviato quando termina l'ultimo thread "normale" (nondaemon), qualcuno chiama System.exit o con altri mezzi specifici della piattaforma (come inviare un SIGINT o premere Ctrl-C).
  2. Mentre sopra è il modo standard e preferito per l'arresto di JVM, può anche essere spento bruscamente chiamando Runtime.halt o uccidendo il processo JVM attraverso il sistema operativo (come l'invio di un SIGKILL).

7

Non si dovrebbe MAI chiamare System.exit(0)per questi motivi:

  1. È un "goto" nascosto e "gotos" interrompono il flusso di controllo. Affidarsi agli hook in questo contesto è una mappatura mentale di cui tutti gli sviluppatori del team devono essere consapevoli.
  2. La chiusura del programma "normalmente" fornisce lo stesso codice di uscita al sistema operativo in System.exit(0)quanto è ridondante.

    Se il tuo programma non può uscire "normalmente" hai perso il controllo del tuo sviluppo [design]. Dovresti avere sempre il pieno controllo dello stato del sistema.

  3. Problemi di programmazione come l'esecuzione di thread non arrestati normalmente vengono nascosti.
  4. È possibile che si verifichi uno stato incoerente dell'applicazione che interrompe i thread in modo anomalo. (Fare riferimento a # 3)

A proposito: la restituzione di codici di ritorno diversi da 0 ha senso se si desidera indicare la chiusura anomala del programma.


2
"Dovresti avere sempre il pieno controllo dello stato del sistema." Questo semplicemente non è possibile in Java. I framework IoC e i server applicazioni sono solo 2 modi molto popolari e ampiamente utilizzati per rinunciare al controllo dello stato del sistema. E va benissimo.
mhlz,

Esegui System.exit (0) nel server delle applicazioni e parlami delle reazioni dell'amministratore del tuo server ... Hai perso l'oggetto di questa domanda e la mia risposta.
oopexpert,

Forse non ero abbastanza chiaro. Dovresti avere sempre il pieno controllo dello stato del sistema di cui sei responsabile. Sì, alcune responsabilità sono state spostate in server applicazioni e IoC. Ma non ne parlavo.
oopexpert,

5

System.exit è necessario

  • quando si desidera restituire un codice di errore diverso da 0
  • quando vuoi uscire dal tuo programma da qualche parte che non è main ()

Nel tuo caso, fa esattamente la stessa cosa del semplice ritorno dal principale.


Cosa intendi con quest'ultimo? Il modo corretto di chiudere il programma è arrestare tutti i thread (in modo corretto), una mossa che non deve essere avviata dal thread principale, quindi non exitè come l'unica opzione disponibile.
Bart van Heukelom,

@Bart: quello che descrivi è uno dei modi possibili per chiudere un programma, non nel modo giusto . Non c'è niente di sbagliato nell'utilizzare gli hook di arresto per fermare tutti i thread in modo corretto. E i ganci di arresto vengono lanciati con exit. Non l'unica opzione, ma sicuramente un'opzione che vale la pena considerare.
Joonas Pulakka,

Ma quello che raccolgo dai documenti, gli hook di arresto sono molto delicati e potrebbero non avere molto tempo per fare ciò che vuoi che facciano. Sicuramente, usali per uscire bene in caso di arresto del sistema operativo o qualcosa del genere, ma se hai intenzione di chiamare System.exit () tu stesso, penso che sia meglio fare il codice di arresto prima di esso (dopo il quale probabilmente non lo farai bisogno di System.exit () più)
Bart van Heukelom,

1
I ganci di arresto hanno tutto il tempo necessario per terminare. La macchina virtuale non viene arrestata prima che tutti gli hook vengano restituiti. È infatti possibile impedire la chiusura della VM registrando un hook di arresto che impiega molto tempo per essere eseguito. Ma ovviamente è possibile eseguire la sequenza di spegnimento in vari modi alternativi.
Joonas Pulakka,

Il ritorno da main non si spegnerà se ci sono altri thread non daemon.
Djechlin,

4

Lo dice Java Language Specification

Uscita dal programma

Un programma termina tutta la sua attività ed esce quando si verifica una di queste due cose:

Tutti i thread che non sono thread daemon terminano.

Alcuni thread invocano il metodo di uscita della classe Runtime o classe System e l'operazione di uscita non è vietata dal gestore della sicurezza.

Significa che dovresti usarlo quando hai un grande programma (beh, per lo meno più grande di questo) e vuoi terminare la sua esecuzione.


3

Se hai un altro programma in esecuzione nella JVM e usi System.exit, anche quel secondo programma verrà chiuso. Si immagini ad esempio che si esegua un lavoro java su un nodo del cluster e che il programma java che gestisce il nodo del cluster venga eseguito nella stessa JVM. Se il lavoro usasse System.exit non solo lascerebbe il lavoro ma anche "spegnerebbe il nodo completo". Non sarebbe possibile inviare un altro lavoro a quel nodo del cluster poiché il programma di gestione è stato chiuso accidentalmente.

Pertanto, non utilizzare System.exit se si desidera poter controllare il proprio programma da un altro programma Java all'interno della stessa JVM.

Utilizzare System.exit se si desidera chiudere di proposito la JVM completa e se si desidera sfruttare le possibilità descritte nelle altre risposte (ad es. Hook di arresto: hook di spegnimento Java , valore di ritorno diverso da zero per la riga di comando chiama: come ottenere lo stato di uscita di un programma Java nel file batch di Windows ).

Dai anche un'occhiata a Runtime Exceptions: System.exit (num) o lancia una RuntimeException da main?

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.