Всегда ли работает блок finally?

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

12 ответов

Решение

От Солнца Учебники

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

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

System.exit завершает работу виртуальной машины.

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

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

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

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

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

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

будет странно и компилировать и возвращать 1.

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

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

catch (OutOfMemoryError oome) {
    // do stuff
}

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

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

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

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

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

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

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

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

Вот некоторые условия, которые могут обойти блок 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

Также, если тупик /livelock происходит внутри 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

и "наконец" никогда не печатается

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

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

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

Есть два способа остановить окончательное выполнение кода блока:
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");  
        }
    }
  }
}

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

Я был удивлен, обнаружив, что блок finally в этом коде действия контроллера вызывается только после исключения, но никогда, когда вызов действительно завершился успешно.

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

Возможно, поток прерывается или что-то, когда вызывается renderBinary(). Я подозреваю, что то же самое происходит для других вызовов render(), но я не проверял это.

Я решил проблему, переместив renderBinary() после try/catch. Дальнейшие исследования показали, что play предоставляет аннотацию @Finally для создания метода, который выполняется после выполнения действия контроллера. Предостережение заключается в том, что это будет вызвано после выполнения ЛЮБОГО действия в контроллере, так что это не всегда может быть хорошим выбором.

Поскольку члены переполнения стека упоминали, что блок finally не будет выполняться, когда:

1: JVM не хватает памяти

2: поток был убит

3: при использовании вложенных блоков try, catch и finally происходит арифметическое исключение, тогда внутреннее окончательно не будет выполнено

 class FinallyExecution{
  public static void main(String [] args){
    try {
        int x = 5;
        System.out.println(x / 0);
        System.out.println("I wont even get executed");
        try {

            String p = null;
            System.out.println(p.length());
        } catch (NullPointerException e) {
            System.out.println("Sorry you got a null in that string");
        } finally {
            System.out.println("What am I doing here?");//this wont execute 
        }
    }
    catch(ArithmeticException e){
        System.out.println("You cannot divide by zero");
    }
    finally{
        System.out.println("I can execute now");
    }
}

}

//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");   
}
}
Другие вопросы по тегам