Un blocco finalmente viene eseguito sempre?


112

C'è qualche condizione in cui finalmente potrebbe non funzionare in java? Grazie.


13
È una domanda che potresti farti quando cerchi di trovare un lavoro con una certa azienda ben nota?
Tom Hawtin - tackline

@ TomHawtin-tackline Ti interessa chiamarlo? (Dio, mi sono perso quanti anni aveva questo post!)
Hele

6
@Hele Non vorrei regalare il gioco, ma puoi cercarlo su Google.
Tom Hawtin - tackline

risposta breve: sì, in condizioni normali.
Squalo

Risposte:


139

dai Sun Tutorials

Nota: se la JVM viene chiusa durante l'esecuzione del codice try o catch, il blocco finalmente potrebbe non essere eseguito. Allo stesso modo, se il thread che esegue il codice try o catch viene interrotto o ucciso, il blocco finalmente potrebbe non essere eseguito anche se l'applicazione nel suo insieme continua.

Non conosco altri modi in cui il blocco finale non sarebbe stato eseguito ...


6
@dhiller - Sono abbastanza sicuro che "power down" sia incluso in "If the JVM exits ..." :-p
Jason Coco

2
@ Jason Coco: Terminare (come in caso di perdita di potenza) non è esattamente la stessa cosa che uscire; il secondo è un processo più o meno organizzato che culmina nel primo. ; p
user359996

2
AFAIK, se un thread viene interrotto, non viene interrotto immediatamente. Spetta al codice nel thread rilevare l'interruzione e interrompere l'attività, quindi alla fine il codice dovrebbe essere eseguito.
Bart van Heukelom

2
Immagino che se viene lanciata un'eccezione nel blocco finalmente, il resto del blocco non viene eseguito.
Adriaan Koster

beh, che schifo!
eiran

63

System.exit arresta la macchina virtuale.

Termina la Java Virtual Machine attualmente in esecuzione. L'argomento funge da codice di stato; per convenzione, un codice di stato diverso da zero indica una terminazione anomala.

Questo metodo chiama il exitmetodo in classe Runtime. Questo metodo non restituisce mai normalmente.

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

"bye" non viene stampato nel codice sopra.


Anche un'eccezione dovrebbe arrestare la Java Virtual Machine.
kaissun

3
Se si verifica un'eccezione durante l'esecuzione di System.exit (0), alla fine verrà eseguito il blocco.
halil

50

Solo per espandere ciò che gli altri hanno detto, tutto ciò che non causa qualcosa come l'uscita della JVM incorrerà nel blocco definitivo. Quindi il seguente metodo:

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

stranamente compilerà e restituirà 1.


2
questo mi ha davvero confuso per un paio d'ore un paio di settimane fa.
nickf

3
È considerata una cattiva idea restituire un valore da un blocco finale. Ritorna solo dal blocco try o torna dall'esterno del blocco try / latest. La maggior parte degli IDE lo contrassegna con un avviso.
Ran Biron

1
@nickf mi sembra di capire che non sei più confuso. Potresti approfondire la meccanica del perché viene restituito 1 e non 0. Potrei solo supporre che la memoria (o è un registro) che memorizza il valore di ritorno della funzione che inizialmente contiene 0, viene sovrascritta quando viene eseguito il blocco finalmente .
Yaneeve,

2
È curioso, in C # non è consentito tornare da un blocco finalmente.
JMCF125

3
@RanBiron Ovviamente. In realtà non stava raccomandando di tornare all'interno di un blocco finalmente, stava solo cercando di dimostrare che anche un'istruzione return causerà comunque l'esecuzione del codice in detto blocco.
Aquarelle

15

In relazione a System.exit, esistono anche alcuni tipi di errori catastrofici in cui un blocco finalmente potrebbe non essere eseguito. Se la JVM esaurisce completamente la memoria, potrebbe semplicemente uscire senza essere catturata o finalmente accadere.

Nello specifico, ricordo un progetto in cui abbiamo stupidamente cercato di utilizzare

catch (OutOfMemoryError oome) {
    // do stuff
}

Questo non ha funzionato perché la JVM non aveva più memoria per eseguire il blocco catch.


Quando viene lanciato OutOfMemoryError, di solito rimane molta memoria (per fermare il thrash di GC). Tuttavia, se lo prendi ripetutamente, ovviamente tornerai a battere GC.
Tom Hawtin - tackline

Ho pensato che non si dovrebbero catturare eccezioni non controllate!
Sergii Shevchyk

1
Ho provato al mio fianco, usando jdk7, ma ha rilevato l'errore OutOfMemory!
Jaskey

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

In tal caso, il fine non verrà eseguito (a meno che il deprecato non Thread.stopvenga chiamato, o un equivalente, ad esempio, tramite un'interfaccia di strumenti).


Questa pagina afferma che viene generato un errore ThreadDeath e che lo stack si svolge normalmente quando viene chiamato Thread.stop (). C'è un problema che mi manca? download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/guide/…
spurserh

Non credo che ci sia un problema. Forse stai immaginando una cattura che non c'è. Se inseriamo un esplicito throw, il finallyblocco verrà eseguito come previsto. try { throw new ThreadDeath(); } finally { System.err.println("?"); }
Tom Hawtin - tackline

9

Il tutorial di Sun è stato citato erroneamente qui in questo thread.

Nota: se la JVM viene chiusa durante l'esecuzione del codice try o catch, il blocco finalmente non verrà eseguito. Allo stesso modo, se il thread che esegue il codice try o catch viene interrotto o ucciso, il blocco finalmente non verrà eseguito anche se l'applicazione nel suo insieme continua.

Se guardi attentamente nel tutorial di sun per il blocco finale, non dice "non verrà eseguito" ma "potrebbe non essere eseguito" Ecco la descrizione corretta

Nota: se la JVM viene chiusa durante l'esecuzione del codice try o catch, il blocco finalmente potrebbe non essere eseguito. Allo stesso modo, se il thread che esegue il codice try o catch viene interrotto o ucciso, il blocco finalmente potrebbe non essere eseguito anche se l'applicazione nel suo insieme continua.

L'apparente ragione di questo comportamento è che la chiamata a system.exit () viene elaborata in un thread di sistema runtime che potrebbe richiedere tempo per chiudere la jvm, nel frattempo lo scheduler del thread può chiedere finalmente di eseguire. Quindi finalmente è progettato per essere eseguito sempre, ma se stai chiudendo jvm, può succedere che jvm si spenga prima di essere finalmente eseguito.


6

Anche se all'interno del tryblocco si verifica un deadlock / livelock .

Ecco il codice che lo dimostra:

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

Questo codice produce il seguente output:

t1 inside lock1
t2 inside lock1

e "finalmente" non viene mai stampato


3
Tecnicamente, il blocco try non esce mai, quindi il blocco latest non dovrebbe mai avere la possibilità di essere eseguito. Lo stesso si potrebbe dire per un ciclo infinito.
Jeff Mercado

6

Se la JVM viene chiusa durante l'esecuzione del codice try o catch, il blocco finalmente potrebbe non essere eseguito. ( fonte )

Arresto normale: si verifica quando viene chiuso l'ultimo thread non daemon OPPURE quando Runtime.exit () ( origine )

Quando un thread esce, la JVM esegue un inventario dei thread in esecuzione e se gli unici thread rimasti sono thread daemon, avvia un arresto ordinato. Quando la JVM si arresta, tutti i thread del daemon rimanenti vengono abbandonati e infine i blocchi non vengono eseguiti, gli stack non vengono svolti e la JVM esce. I thread daemon dovrebbero essere usati con parsimonia, poche attività di elaborazione possono essere abbandonate in modo sicuro in qualsiasi momento senza alcuna pulizia. In particolare, è pericoloso utilizzare thread daemon per attività che potrebbero eseguire qualsiasi tipo di I / O. I thread daemon vengono salvati meglio per le attività di "manutenzione", come un thread in background che rimuove periodicamente le voci scadute da una cache in memoria. ( fonte )

L'ultimo thread non daemon esce dall'esempio:

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

Produzione:

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

Nei seguenti casi, infine, il blocco non verrà eseguito: -

  • Quando System.exit(0)viene richiamato dal tryblocco.
  • Quando JVM esaurisce la memoria
  • Quando il tuo processo java viene ucciso forzatamente da task mgr o console
  • Condizione di deadlock nel tuo tryblocco
  • Quando la macchina si spegne a causa di un'interruzione di corrente

Potrebbero esserci anche altri casi marginali, in cui il blocco alla fine non verrà eseguito.


1

Ci sono due modi per interrompere definitivamente l'esecuzione del codice:
1. Usare System.exit ();
2. Se in qualche modo il controllo dell'esecuzione non raggiunge per provare a bloccare.
Vedere:

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

Mi sono imbattuto in un caso molto specifico del blocco finale non in esecuzione correlato specificamente al framework di gioco.

Sono stato sorpreso di scoprire che il blocco finalmente in questo codice di azione del controller è stato chiamato solo dopo un'eccezione, ma mai quando la chiamata è riuscita effettivamente.

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

Forse il thread viene terminato o qualcosa del genere quando viene chiamato renderBinary (). Sospetto che la stessa cosa accada per altre chiamate render (), ma non l'ho verificato.

Ho risolto il problema spostando renderBinary () dopo il try / catch. Ulteriori indagini hanno rivelato che il gioco fornisce un'annotazione @Finally per creare un metodo che viene eseguito dopo l'esecuzione di un'azione del controller. L'avvertenza qui è che questo verrà chiamato dopo l'esecuzione di QUALSIASI azione nel controller, quindi potrebbe non essere sempre una buona scelta.


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

Migliora il rientro e aggiungi alcuni dettagli.
ROMANIA_engineer

1
L'esecuzione non arriverebbe nemmeno al blocco try interno, ovviamente quello interno alla fine non verrebbe eseguito.
Anton Arhipov
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.