Чи завжди працює остаточний блок?


112

Чи є якась умова, коли нарешті може не працювати в java? Дякую.


13
Це питання, яке вам можуть задати, намагаючись влаштуватися на роботу в певну відому компанію?
Том Хотін - тайклін

@ TomHawtin-tackline Догляд, щоб його назвати? (Боже, я пропустив, скільки років було цій посаді!)
Хеле,

6
@Hele Я не хотів би віддавати гру, але ви можете її гугл.
Том Хотін - тайклін

коротка відповідь: так, у звичайних умовах.
Акула

Відповіді:


139

з Підручників із Сонця

Примітка: Якщо JVM закривається під час виконання пробного коду, блок остаточного блоку може не виконатись. Так само, якщо потік, який виконує код спробу вилову, перерваний або знищений, остаточний блок може не виконатись, хоча програма в цілому продовжується.

Я не знаю жодних інших способів, нарешті, блок не був би виконаний ...


6
@dhiller - Я майже впевнений, що "вимкнення живлення" включене в "Якщо JVM закінчується ..." :-p
Джейсон Коко

2
@ Джейсон Коко: Закінчення (як і після втрати влади) - не зовсім те саме, що вихід; остання є більш-менш організованим процесом, кульмінацією якого є перший. ; p
користувач359996

2
AFAIK, якщо нитка перервана, вона не одразу зупиняється. Це до коду в потоці, щоб виявити переривання і зупинити його завдання, тому нарешті код повинен запуститися.
Барт ван Хекелом

2
Я здогадуюсь, що якщо виняток буде кинутий у остаточному блоці, решта блоку не виконується.
Адріан Костер

добре, що смокче!
ейран

63

System.exit вимикає віртуальну машину.

Припиняє працюючий на даний момент віртуальний апарат Java. Аргумент служить кодом статусу; за умовою, ненульовий код статусу вказує на аномальне припинення.

Цей метод викликає exitметод у класі Runtime. Цей метод ніколи не повертається нормально.

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

"до побачення" не друкується у наведеному вище коді.


Також виняток повинен закрити віртуальну машину Java.
kaissun

3
Якщо виняток виникає під час виконання System.exit (0), то, нарешті, блок буде виконаний, хоча.
халіл

50

Просто для розширення того, що сказали інші, все, що не спричинить щось на кшталт виходу JVM, спричинить остаточний блок. Отже, наступний метод:

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

дивним чином і компілюють, і повернуть 1.


2
це насправді збентежило мене за гарну пару годин кілька тижнів тому.
nickf

3
Поганою ідеєю вважається повернення значення з остаточного блоку. Або повертайтеся лише з блоку спробу, або повертайтеся поза блоком спробувати / нарешті. Більшість ІДЕ позначать це попередженням.
Ран Бірон

1
@nickf Я вважаю, що вас більше не бентежить. Не могли б ви детальніше пояснити механізм того, чому повертається 1, а не 0. Я можу лише здогадуватися, що пам'ять (або це реєстр), яка зберігає повернене значення функції, яка спочатку містить 0, перезаписується, коли остаточний блок виконується .
Янєєве

2
Це цікаво, що в C # заборонено повертатися з остаточного блоку.
JMCF125

3
@RanBiron Звичайно. Він насправді не рекомендував повернутися всередині остаточного блоку, він лише намагався продемонструвати, що навіть заява про повернення все ще призведе до виконання коду у згаданому блоці.
Акварель

15

Пов’язані з System.exit, також існують певні типи катастрофічних збоїв, коли нарешті блок може не виконуватись. Якщо у JVM повністю вичерпано пам'ять, він може просто вийти без вилову або, нарешті, відбутися.

Зокрема, я пам’ятаю проект, який ми нерозумно намагалися використати

catch (OutOfMemoryError oome) {
    // do stuff
}

Це не спрацювало, оскільки у JVM не залишилося пам'яті для виконання блоку вилову.


Коли OutOfMemoryError кинуто, зазвичай залишається багато пам'яті (щоб зупинити обмін GC). Однак, якщо ви їх ловите неодноразово, ви, очевидно, повернетесь до гримування GC.
Том Хотін - тайклін

Я думав, що не слід ловити неперевірені винятки!
Сергій Шевчик

1
Я спробував на моєму боці, використовуючи jdk7, але він виявив помилку OutOfMemory!
Jaskey

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

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


Ця сторінка стверджує, що помилка ThreadDeath закидається і що стек розгортається нормально, коли викликається Thread.stop (). Чи є улов, який мені не вистачає? download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/guide/…
spurserh

Я не думаю, що тут є улов. Можливо, там ви уявляєте улов, якого там немає. Якщо ми помістимо явний throw, тоді finallyблок буде виконаний так, як очікувалося. try { throw new ThreadDeath(); } finally { System.err.println("?"); }
Том Хотін - тайклін

9

Підручник «Сонце» помилково цитується тут у цій темі.

Примітка: Якщо виходи JVM , а спроба зловити або код виконується, то в кінці кінців блок буде не виконувати. Точно так же, якщо потік виконання TRY або зловити код переривається або убитий, нарешті , блок буде не виконувати , навіть якщо додаток в цілому триває.

Якщо ви уважно придивитесь до навчального посібника, щоб остаточно заблокувати, він не говорить "не виконає", але "може не виконати" Ось правильний опис

Примітка: Якщо JVM закривається під час виконання пробного коду, блок остаточного блоку може не виконатись. Так само, якщо потік, який виконує код спробу вилову, перерваний або знищений, остаточний блок може не виконатись, хоча програма в цілому продовжується.

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


6

Також якщо всередині 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

і "нарешті" ніколи не друкується


3
Технічно блок спробу ніколи не закінчується, тому остаточний блок ніколи не повинен отримати шанс на виконання. Те саме можна сказати і про нескінченну петлю.
Джефф Меркадо

6

Якщо JVM закінчується під час виконання коду спробу вилову, блок нарешті може не виконатись. ( джерело )

Звичайне завершення роботи - це відбувається або тоді, коли останній недемонний потік закінчується АБО, коли Runtime.exit () ( джерело )

Коли потік виходить, JVM виконує інвентаризацію запущених потоків, і якщо єдині залишки, що залишилися, - це демонові потоки, він ініціює впорядковане відключення. Коли JVM зупиняється, залишки будь-яких демонових ниток остаточно блоки не виконуються, стеки не розкручуються, JVM просто виходить. Нитки Daemon слід використовувати рідко, мало обробних операцій можна безпечно відмовитись у будь-який час без очищення. Зокрема, небезпечно використовувати нитки демона для завдань, які можуть виконувати будь-який тип вводу-виводу. Нитки Daemon найкраще зберігати для виконання завдань "ведення домашнього господарства", таких як фонова нитка, яка періодично видаляє закінчені записи з кеша пам'яті. ( джерело )

Останній недемонний потік виходить із прикладу:

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

У наступних випадках остаточно блок не буде виконаний: -

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

Можуть бути й інші випадки бахроми, коли нарешті блок не буде виконаний.


1

Є два способи зупинити остаточне блокування виконання коду:
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

Я натрапив на дуже конкретний випадок, коли нарешті блок не виконується, пов'язаний конкретно з ігровою рамкою.

Я здивовано виявив, що остаточний блок цього коду дії контролера викликається лише після винятку, але ніколи, коли виклик насправді не вдався.

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

Можливо, нитка припиняється або щось, коли викликається renderBinary (). Я б підозрював, що те ж саме відбувається і з іншими дзвінками render (), але я не підтвердив це.

Я вирішив проблему, перемістивши renderBinary () на спробу / catch. Подальше дослідження показало, що гра надає анотацію @Finally для створення методу, який виконується після виконання дії контролера. Тут заперечення полягає в тому, що це буде викликано після виконання будь-яких дій у контролері, тому це не завжди може бути вдалим вибором.


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

Удосконаліть відступ і додайте деякі деталі.
ROMANIA_engineer

1
Виконання навіть не потрапить до внутрішнього блоку спробу, звичайно, внутрішній нарешті не буде виконаний.
Антон Архипов
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.