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


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

12 100

12 ответов:

с Sun Tutorials

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

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

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

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

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

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

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

catch (OutOfMemoryError oome) {
    // do stuff
}

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

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

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

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

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

Если вы внимательно посмотрите на Sun tutorial для finally block, Он не говорит "не будет выполнить" но " может не выполняться" Вот правильное описание

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

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

также, если тупик / 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:

  1. если JVM завершает работу во время выполнения кода try или catch, то блок finally может не выполняться.
  2. нормальное завершение работы-это происходит либо при выходе последнего потока без демона, либо во время выполнения.exit ()
  3. при выходе из потока JVM выполняет инвентаризацию запущенных потоков, и если остались только потоки daemon потоки, он инициирует упорядоченное завершение работы. Когда 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

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

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

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

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

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

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

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

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

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

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