Всегда ли работает блок finally? - PullRequest
110 голосов
/ 21 января 2009

Есть ли какое-нибудь условие, когда, наконец, может не работать в Java? Спасибо.

Ответы [ 12 ]

136 голосов
/ 21 января 2009

из Sun Tutorials

Примечание. Если JVM завершает работу во время попытки или код перехвата выполняется, тогда блок finally может не выполняться. Аналогично, если поток, выполняющий попробуйте или поймать код прерывается или убит, наконец, блок не может выполнить, даже если приложение как целое продолжается.

Я не знаю других способов, чтобы блок finally не выполнялся ...

62 голосов
/ 21 января 2009

System.exit выключает виртуальную машину.

Завершает работающую в данный момент Java Виртуальная машина. Аргумент служит в качестве кода состояния; условно ненулевой код состояния указывает на ненормальное завершение.

Этот метод вызывает метод exit в класс Runtime. Этот метод никогда возвращается нормально.

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

"пока" не распечатывается в вышеуказанном коде.

49 голосов
/ 21 января 2009

Просто чтобы расширить то, что сказали другие, все, что не вызывает нечто вроде выхода из JVM, повлечет за собой блок finally. Итак, следующий метод:

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

странно и скомпилирует и вернет 1.

15 голосов
/ 21 января 2009

Относительно System.exit существуют также некоторые типы катастрофических сбоев, когда блок finally может не выполняться. Если JVM не хватает памяти полностью, он может просто выйти без перехвата или, наконец, произойдет.

В частности, я помню проект, в котором мы глупо пытались использовать

catch (OutOfMemoryError oome) {
    // do stuff
}

Это не сработало, потому что у JVM не осталось памяти для выполнения блока catch.

10 голосов
/ 21 января 2009
try { for (;;); } finally { System.err.println("?"); }

В этом случае finally не будет выполнено (если не вызывается устаревший Thread.stop или эквивалент, скажем, через интерфейс инструментов).

8 голосов
/ 30 апреля 2010

Учебник Sun был ошибочно процитирован здесь в этой теме.

Примечание. Если JVM завершает работу во время выполнения кода try или catch, блок finally не будет выполняться. Аналогично, если поток, выполняющий код попытки или перехвата, прерван или прерван, блок finally не будет выполняться, даже если приложение в целом продолжит работу.

Если вы внимательно изучите учебник по солнцу для блока finally, в нем не будет «не выполнится», но «может не исполниться» Вот правильное описание

Примечание. Если JVM завершает работу во время выполнения кода try или catch, блок finally может не выполняться. Аналогично, если поток, выполняющий код try или catch, прерывается или уничтожается, блок finally может не выполняться, даже если приложение в целом продолжается.

Очевидная причина такого поведения заключается в том, что вызов system.exit () обрабатывается в системном потоке времени выполнения, для завершения которого может потребоваться время, а планировщик потока может запросить, наконец, выполнить. Таким образом, finally предназначено для постоянного выполнения, но если вы закрываете jvm, может случиться, что jvm завершит работу, прежде чем наконец выполнится.

6 голосов
/ 20 апреля 2011

Также, если внутри блока try возникает тупик / живая блокировка.

Вот код, который демонстрирует это:

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

Этот код выдает следующий вывод:

t1 inside lock1
t2 inside lock1

и «наконец-то» никогда не печатается

5 голосов
/ 08 сентября 2011

Вот некоторые условия, которые могут обойти блок finally:

  1. Если JVM завершает работу во время выполнения кода try или catch, блок finally может не выполняться.
  2. Нормальное выключение - это происходит либо при выходе из последнего потока, не являющегося демоном, либо при выполнении Runtime.exit ()
  3. При выходе из потока JVM выполняет инвентаризацию запущенных потоков, и, если остались только потоки, это потоки демона, он инициирует упорядоченное завершение работы. Когда JVM останавливается, все оставшиеся потоки демона прекращаются, и, наконец, блоки не выполняются, стеки не свернуты, JVM просто завершается. Потоки демонов следует использовать экономно, но некоторые операции обработки можно безопасно отменить в любое время без очистки. В частности, опасно использовать потоки демона для задач, которые могут выполнять любые операции ввода-вывода. Потоки демона лучше всего сохранять для «вспомогательных» задач, таких как фоновый поток, который периодически удаляет просроченные записи из кэша в памяти.

Последний выходящий из демона поток выходит из примера:

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

Выход:

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 голос
/ 14 сентября 2018

Есть два способа окончательно остановить выполнение кода блока:
1. Используйте System.exit ();
2. Если каким-то образом контроль выполнения не доходит, чтобы попробовать блокировать.
См:

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 голосов
/ 04 ноября 2016

В следующих случаях блок finally не будет выполнен: -

  • Когда System.exit(0) вызывается из блока try.
  • Когда JVM не хватает памяти
  • Когда ваш Java-процесс принудительно убит из задачи mgr или из консоли
  • Состояние тупика в вашем try блоке
  • Когда ваша машина выключается из-за сбоя питания

Могут быть и другие дополнительные случаи, когда в конечном итоге блок не будет выполнен.

...