Czy ostatecznie blok zawsze działa?


112

Czy jest jakiś warunek, w którym ostatecznie może nie działać w Javie? Dzięki.


13
Czy jest to pytanie, które możesz zadać, próbując znaleźć pracę w pewnej dobrze znanej firmie?
Tom Hawtin - tackline

@ TomHawtin-tackline Chcesz go nazwać? (Boże, przegapiłem, ile lat miał ten post!)
Hele

6
@Hele Nie chciałbym oddać tej gry, ale możesz ją wygooglować.
Tom Hawtin - tackline

krótka odpowiedź: tak, w normalnych warunkach.
Shark

Odpowiedzi:


139

z samouczków Sun

Uwaga: Jeśli maszyna JVM zakończy działanie podczas wykonywania kodu try lub catch, blok final może nie zostać wykonany. Podobnie, jeśli wątek wykonujący kod try lub catch zostanie przerwany lub zabity, ostatni blok może nie zostać wykonany, nawet jeśli aplikacja jako całość będzie kontynuowana.

Nie znam innych sposobów, w jakie ostateczny blok nie zostałby wykonany ...


6
@dhiller - Jestem prawie pewien, że „wyłączanie zasilania” jest uwzględnione w sekcji „Jeśli JVM kończy pracę ...” :-p
Jason Coco

2
@Jason Coco: Zakończenie (jak w przypadku utraty zasilania) to nie to samo, co wyjście; ten drugi jest mniej lub bardziej zorganizowanym procesem, którego kulminacją jest ten pierwszy. ; p
user359996

2
AFAIK, jeśli wątek zostanie przerwany, nie jest natychmiast zatrzymywany. To do kodu w wątku należy wykrycie przerwania i zatrzymanie zadania, więc w końcu kod powinien zostać uruchomiony.
Bart van Heukelom

2
Myślę, że jeśli wyjątek zostanie zgłoszony w ostatnim bloku, reszta bloku nie zostanie wykonana.
Adriaan Koster

cóż, to jest do bani!
eiran

63

System.exit wyłącza maszynę wirtualną.

Kończy aktualnie uruchomioną wirtualną maszynę Java. Argument służy jako kod statusu; Zgodnie z konwencją niezerowy kod statusu wskazuje na nieprawidłowe zakończenie.

Ta metoda wywołuje exitmetodę w klasie Runtime. Ta metoda nigdy nie zwraca normalnie.

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

„Do widzenia” nie jest drukowane w powyższym kodzie.


Wyjątek powinien również spowodować zamknięcie wirtualnej maszyny języka Java.
Kaissun

3
Jeśli wyjątek wystąpi podczas wykonywania System.exit (0), to ostatecznie blok zostanie wykonany.
halil

50

Aby rozwinąć to, co powiedzieli inni, wszystko, co nie spowoduje czegoś takiego jak zamknięcie maszyny JVM, spowoduje ostateczną blokadę. Więc następująca metoda:

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

będzie dziwnie kompilować i zwracać 1.


2
to naprawdę zdezorientowało mnie przez dobre kilka godzin kilka tygodni temu.
nickf

3
Zwracanie wartości z końcowego bloku jest uważane za zły pomysł. Powróć tylko z bloku try lub wróć spoza bloku try / final. Większość IDE zaznaczy to ostrzeżeniem.
Biegałem z Bironem

1
@nickf Rozumiem, że nie jesteś już zdezorientowany. Czy mógłbyś rozwinąć mechanikę, dlaczego zwracana jest 1, a nie 0. Mogę się tylko domyślać, że pamięć (lub jest to rejestr), która przechowuje wartość zwracaną przez funkcję, która początkowo zawiera 0, jest nadpisywana podczas wykonywania ostatniego bloku .
Yaneeve

2
To ciekawe, w C # nie można wrócić z końcowego bloku.
JMCF125

3
@RanBiron Oczywiście. W rzeczywistości nie zalecał powrotu wewnątrz bloku Final, starał się tylko zademonstrować, że nawet instrukcja return nadal spowoduje wykonanie kodu w tym bloku.
Aquarelle

15

W przypadku System.exit istnieją również pewne typy katastrofalnych awarii, w przypadku których ostateczna blokada może nie zostać wykonana. Jeśli JVM całkowicie zabraknie pamięci, może po prostu zakończyć działanie bez przechwytywania lub w końcu się dzieje.

W szczególności pamiętam projekt, w którym głupio próbowaliśmy wykorzystać

catch (OutOfMemoryError oome) {
    // do stuff
}

To nie zadziałało, ponieważ JVM nie miała pamięci na wykonanie bloku catch.


Kiedy wyrzucany jest OutOfMemoryError, zwykle pozostaje dużo pamięci (aby zatrzymać rzucanie GC). Jeśli jednak złapiesz go wielokrotnie, oczywiście wrócisz do rzucania w GC.
Tom Hawtin - tackline

Pomyślałem, że nie powinno się łapać niezaznaczonych wyjątków!
Sergii Shevchyk

1
Próbowałem po mojej stronie, używając jdk7, ale złapał błąd OutOfMemory!
Jaskey,

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

W takim przypadku ostatecznie nie zostanie wykonany (chyba że Thread.stopzostanie wywołany przestarzały lub jego odpowiednik, powiedzmy, za pośrednictwem interfejsu narzędzi).


Ta strona twierdzi, że zgłaszany jest błąd ThreadDeath i że stos jest rozwijany normalnie po wywołaniu Thread.stop (). Czy jest jakiś haczyk, którego mi brakuje? download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/guide/…
spurserh

Nie sądzę, że jest jakiś haczyk. Być może wyobrażasz sobie haczyk, którego nie ma. Jeśli wstawimy jawne throw, finallyblok zostanie wykonany zgodnie z oczekiwaniami. try { throw new ThreadDeath(); } finally { System.err.println("?"); }
Tom Hawtin - tackline

9

Samouczek Sun został źle zacytowany w tym wątku.

Uwaga: Jeśli maszyna JVM zakończy działanie podczas wykonywania kodu try lub catch, blok final nie zostanie wykonany. Podobnie, jeśli wątek wykonujący kod try lub catch zostanie przerwany lub zabity, ostatni blok tak zrobi nie wykonany, nawet jeśli aplikacja jako całość będzie kontynuowana.

Jeśli przyjrzysz się uważnie samouczkowi Sun dla końcowego bloku, nie jest tam napisane "nie wykona", ale "może nie wykonać" Oto poprawny opis

Uwaga: Jeśli maszyna JVM zakończy działanie podczas wykonywania kodu try lub catch, blok final może nie zostać wykonany. Podobnie, jeśli wątek wykonujący kod try lub catch zostanie przerwany lub zabity, ostatni blok może nie zostać wykonany, nawet jeśli aplikacja jako całość będzie kontynuowana.

Oczywistym powodem takiego zachowania jest to, że wywołanie system.exit () jest przetwarzane w wątku systemowym środowiska wykonawczego, co może zająć trochę czasu, aby zamknąć jvm, w międzyczasie planista wątków może zażądać ostatecznego wykonania. Ostatecznie jest zaprojektowany tak, aby zawsze był wykonywany, ale jeśli zamykasz jvm, może się zdarzyć, że jvm zostanie zamknięty przed ostatecznym wykonaniem.


6

Również wtedy, gdy w środku zdarzy się zakleszczenie / żywa żywa ryba try .

Oto kod, który to demonstruje:

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

Ten kod generuje następujące dane wyjściowe:

t1 inside lock1
t2 inside lock1

a „w końcu” nigdy nie jest drukowane


3
Technicznie rzecz biorąc, blok try nigdy nie wychodzi, więc ostatni blok nie powinien mieć szansy na wykonanie. To samo można powiedzieć o nieskończonej pętli.
Jeff Mercado,

6

Jeśli maszyna JVM zakończy działanie podczas wykonywania kodu try lub catch, blok final może nie zostać wykonany. ( źródło )

Normalne zamknięcie - dzieje się tak, gdy ostatni wątek niebędący demonem kończy działanie LUB gdy Runtime.exit () ( źródło )

Po zakończeniu wątku maszyna JVM przeprowadza inwentaryzację uruchomionych wątków, a jeśli jedynymi pozostałymi wątkami są wątki demonów, inicjuje uporządkowane zamknięcie. Kiedy JVM zatrzymuje się, wszystkie pozostałe wątki demona są porzucane, a wreszcie bloki nie są wykonywane, stosy nie są rozwijane, JVM po prostu kończy pracę. Wątki demona powinny być używane oszczędnie. Niewiele czynności przetwarzania można bezpiecznie porzucić w dowolnym momencie bez czyszczenia. W szczególności niebezpieczne jest używanie wątków demonów do zadań, które mogą wykonywać dowolne operacje we / wy. Wątki demona najlepiej zapisywać do zadań „porządkowych”, takich jak wątek w tle, który okresowo usuwa wygasłe wpisy z pamięci podręcznej w pamięci. ( źródło )

Przykład wyjścia ostatniego wątku niebędącego demonem:

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

Wynik:

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

W następujących przypadkach ostateczny blok nie zostanie wykonany: -

  • Kiedy System.exit(0)jest wywoływana z trybloku.
  • Kiedy zabraknie pamięci JVM
  • Gdy proces java zostanie wymuszony zabity z poziomu menedżera zadań lub konsoli
  • Stan impasu w twoim trybloku
  • Gdy urządzenie wyłączy się z powodu awarii zasilania

Mogą również istnieć inne skrajne przypadki, w których ostatecznie blok nie zostanie wykonany.


1

Istnieją dwa sposoby, aby ostatecznie zatrzymać wykonywanie kodu blokowego:
1. Użyj System.exit ();
2. Jeśli w jakiś sposób kontrola wykonania nie osiągnie, spróbuj zablokować.
Widzieć:

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

Natknąłem się na bardzo specyficzny przypadek, w którym ostatni blok nie jest wykonywany, związany konkretnie z frameworkiem odtwarzania.

Zaskoczyło mnie, że ostatni blok w tym kodzie akcji kontrolera został wywołany tylko po wystąpieniu wyjątku, ale nigdy, gdy wywołanie faktycznie się powiodło.

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

Być może wątek jest zakończony lub coś innego, gdy zostanie wywołana funkcja renderBinary (). Podejrzewałbym, że to samo dzieje się z innymi wywołaniami render (), ale nie zweryfikowałem tego.

Rozwiązałem ten problem, przenosząc renderBinary () na koniec try / catch. Dalsze dochodzenie ujawniło, że gra udostępnia adnotację @Finally, aby utworzyć metodę, która jest wykonywana po wykonaniu akcji kontrolera. Zastrzeżenie polega na tym, że zostanie to wywołane po wykonaniu DOWOLNEJ akcji w kontrolerze, więc nie zawsze może to być dobry wybór.


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

Popraw wcięcie i dodaj kilka szczegółów.
ROMANIA_engineer

1
Wykonanie nie dotarło nawet do wewnętrznego bloku try, oczywiście wewnętrzny ostatecznie nie zostałby wykonany.
Anton Arhipov
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.