Коли ми повинні викликати System.exit на Java


193

У Java Яка різниця із чи без System.exit(0)наступного коду?

public class TestExit
{      
    public static void main(String[] args)
    { 
        System.out.println("hello world");

        System.exit(0);  // is it necessary? And when it must be called? 
    }      
}

У документі сказано: "Цей метод ніколи не повертається нормально". Що це означає?

Відповіді:


207

System.exit()можна використовувати для запуску гаків відключення до завершення програми. Це зручний спосіб вирішити питання про відключення у великих програмах, коли всі частини програми не можуть (і не повинні) усвідомлювати одна одну. Потім, якщо хтось хоче вийти, він може просто зателефонувати System.exit(), а гачки закриття (якщо правильно встановлені) дбають про виконання всіх необхідних церемоній закриття, таких як закриття файлів, звільнення ресурсів тощо.

"Цей метод ніколи не повертається нормально." означає просто, що метод не повернеться; як тільки нитка піде туди, вона не повернеться.

Інший, можливо, більш поширений спосіб виходу з програми - це просто дійти до кінця mainметоду. Але якщо будуть запущені якісь недемонові потоки, вони не закриються, і таким чином JVM не вийде. Таким чином, якщо у вас є якісь такі недемонові потоки, вам знадобляться інші засоби (крім гачків відключення), щоб вимкнути всі недемонові потоки та звільнити інші ресурси. Якщо немає інших недемонових потоків, повернення з mainвимкне JVM і зателефонує гачками відключення.

Чомусь гачки відключення здаються недооціненим і неправильно зрозумілим механізмом, і люди заново вигадують колесо за допомогою всіляких фірмових хакерських хакерів, щоб вийти зі своїх програм. Я б закликав використовувати гачки для відключення; все це є в стандартному режимі виконання, який ви все одно будете використовувати.


10
"Цей метод ніколи не повертається нормально." означає просто, що метод не повернеться; як тільки нитка піде туди, вона не повернеться. Зокрема, зверніть увагу, що це означає, що ви не можете перевірити метод, який робить виклик System.exit (0) ...
Білл Мішелл

5
-1 Неправильно. Гаки відключення запускаються, якщо JVM закінчується нормально, незалежно від того, що це через System.exit або припинення main (). Дивіться zx81 / doku / java / javadoc / j2se1.5.0 / docs / api / java / lang /…
sleske

31
@sleske: припинення main () недостатньо, якщо навколо є інші недемонові нитки. Вимкнення починається лише після завершення останнього недемонного потоку, якщо ви явно не викликаєте System.exit (). Це чітко зазначено в документації на виконання.
Joonas Pulakaka

3
Зауважте, що якщо ваш гак відключення, в свою чергу, спирається на потік, який називається System.exit, ви зайшли в тупик.
djechlin

8
Що ще додати, якщо хтось зупинить час виконання, гачки відключення не працюватимуть ( Runtime.getRuntime().halt())
Rogue

50

У такому випадку це не потрібно. Ніяких зайвих потоків не буде запущено, ви не змінюєте код виходу (який за замовчуванням дорівнює 0) - в основному це безглуздо.

Коли документи кажуть, що метод ніколи не повертається нормально, це означає, що наступний рядок коду фактично недоступний, хоча компілятор не знає, що:

System.exit(0);
System.out.println("This line will never be reached");

Буде викинуто виняток, або VM припиниться перед поверненням. Це ніколи "просто не повернеться".

Дуже рідко варто звернутися до System.exit()IME. Це може мати сенс, якщо ви пишете інструмент командного рядка, і хочете вказати помилку за допомогою коду виходу, а не просто викидати виняток ... але я не можу пригадати, коли останній раз я використовував його у звичайному виробничому коді .


2
Чому компілятор цього не знає? Чи не System.exit () достатньо спеціальний, щоб гарантувати конкретний код виявлення?
Барт ван Хекелом

3
@Bart: Ні, я не думаю, що так. Розміщення особливих випадків у мові для таких речей додає мовної складності з дуже невеликою користю.
Джон Скіт

Я думав, що "ніколи не повертається нормально" пов'язаний із "швидким завершенням" заяви. (Розділ 14.1 JLS). Я помиляюся?
aioobe

@aioobe: Ні, ти маєш рацію. System.exit()ніколи не завершується нормально - він завжди буде завершений за винятком, або з відключенням VM (що насправді не стосується JLS).
Джон Скіт

1
@piechuckerr: Що змушує тебе це думати? Цілком розумно називати його зі значенням, відмінним від 0, вказувати оболонці виклику (або будь-якому іншому), що програма виявила помилку.
Джон Скіт

15

System.exit(0)припиняє СВМ. У таких простих прикладах важко зрозуміти різницю. Параметр передається назад в ОС і, як правило, використовується для вказівки на аномальне припинення (наприклад, якась фатальна помилка), тому, якщо ви викликали java з пакетного файлу або сценарію оболонки, ви зможете отримати це значення та отримати уявлення якщо заявка була успішною.

Це мало би вплинути, якщо ви зателефонували System.exit(0)на додаток, розгорнутий на сервер додатків (подумайте про це, перш ніж спробувати).


15

Метод ніколи не повертається, тому що це кінець світу, і жоден ваш код не буде виконаний далі.

Ваша програма у вашому прикладі все одно виходитиме з того самого місця в коді, але, якщо ви використовуєте System.exit. у вас є можливість повернути спеціальний код до навколишнього середовища, наприклад, скажімо

System.exit(42);

Хто збирається використати ваш вихідний код? Сценарій, який викликав додаток. Працює в Windows, Unix та всіх інших середовищах для написання сценарію.

Навіщо повертати код? Сказати такі речі, як "Мені не вдалося", "База даних не відповіла".

Щоб дізнатися, як отримати значення коду виходу та використовувати його в скрипті оболонки Unix або скрипті Windows cmd, ви можете перевірити цю відповідь на цьому веб-сайті


12

У додатках, які можуть мати складні гачки відключення, цей метод не слід викликати з невідомої нитки. System.exitніколи не виходить нормально, тому що виклик буде заблокований, поки JVM не припиняється. Це як би який би не працював код, на якому натягнуто шнур живлення, перш ніж він може закінчитися. Виклик System.exitініціює гачки вимкнення програми та будь-яку нитку, яка дзвінки System.exitбуде блокувати до завершення програми. Це означає, що якщо гак відключення, в свою чергу, подає завдання на потік, з якого System.exitвикликався, програма перейде в глухий кут.

Я обробляю це у своєму коді із наступним:

public static void exit(final int status) {
    new Thread("App-exit") {
        @Override
        public void run() {
            System.exit(status);
        }
    }.start();
}

У мене також виникла проблема, коли System.exit насправді не закінчив JVM. Однак це відбувається лише за певних умов. Я взяв потоковий потік не дав мені зрозуміти, які обробники потоків / вимикань блокують закриття. Використання коду, описаного в коментарі, допомогло мені вирішити!
Кай

7

Хоча відповідь була дуже корисною, але дехто пропустив ще кілька додаткових деталей. Сподіваюсь, що нижче допоможе зрозуміти процес вимкнення в Java, на додаток до відповіді вище:

  1. При впорядкованому відключенні * JVM спочатку запускає всі зареєстровані гачки відключення. Гачки відключення - це непроменені нитки, які зареєстровані за допомогою Runtime.addShutdownHook.
  2. JVM не дає гарантій на порядок запуску гачків відключення. Якщо будь-які потоки додатків (демон чи недемон) все ще працюють під час відключення, вони продовжують працювати одночасно з процедурою відключення .
  3. Коли всі гачки відключення завершені, JVM може вибрати запуск фіналізаторів, якщо runFinalizersOnExit вірно, а потім зупиняється.
  4. JVM не робить спроб зупинити або перервати будь-які потоки додатків, які все ще працюють під час відключення; вони різко припиняються, коли з часом зупиняється СП.
  5. Якщо відключення гаків або фіналізаторів не завершиться, процес впорядкованого вимкнення "зависає", і JVM необхідно різко відключити.
  6. При різкому відключенні СВМ не вимагає нічого іншого, крім зупинки СВМ; гачки відключення не працюватимуть.

PS: JVM може відключитися як впорядковано, так і різко .

  1. Впорядковане відключення ініціюється, коли завершується останній "нормальний" (недемон) потоковий потік, хтось викликає System.exit, або іншими специфічними для платформи способами (наприклад, надсиланням SIGINT або натисканням Ctrl-C).
  2. Хоча вище є стандартним і бажаним способом відключення JVM, його також можна різко відключити, зателефонувавши до Runtime.halt або вбивши процес JVM через операційну систему (наприклад, відправлення SIGKILL).

7

НІКОЛИ не слід закликати System.exit(0)з таких причин:

  1. Це приховане "гото" і "гото" порушують контрольний потік. Опираючись на гачки в цьому контексті, це розумове відображення, про яке повинен розуміти кожен розробник у колективі.
  2. Вихід із програми "нормально" надає той же код виходу в операційну систему, як System.exit(0)і це зайве.

    Якщо ваша програма не може вийти «нормально», ви втратили контроль над своєю розробкою [дизайн]. Ви повинні завжди мати повний контроль над станом системи.

  3. Проблеми з програмуванням, такі як запущені потоки, які не зупиняються, зазвичай приховуються.
  4. Ви можете зіткнутися з неузгодженим станом програми, що перериває потоки аномально. (Див. №3)

До речі: Повернення інших кодів повернення, ніж 0, має сенс, якщо ви хочете вказати на ненормальне припинення програми.


2
"Ви повинні завжди мати повний контроль над станом системи." На Java це просто неможливо. Рамки і сервери додатків IoC - це лише 2 дуже популярні та широко використовувані способи, коли ви відмовитеся від контролю над станом системи. І це абсолютно чудово.
mhlz

Будь ласка, запустіть System.exit (0) на своєму сервері додатків та розкажіть мені про реакції вашого адміністратора сервера ... Ви пропустили тему цього питання та мою відповідь.
oopexpert

Можливо, я був недостатньо зрозумілий. Ви повинні завжди мати повний контроль над системним станом, за який ви несете відповідальність. Так, деякі обов'язки були перенесені на сервери додатків та IoC. Але я не говорив про це.
oopexpert

5

System.exit потрібен

  • коли ви хочете повернути код помилки без 0
  • коли ви хочете вийти зі своєї програми звідкись не головного ()

У вашому випадку це робить саме те саме, що і просте повернення з основного.


Що ви маєте на увазі під останнім? Правильний спосіб вимкнення програми - це зупинка всіх потоків (красиво), рух, який не потрібно ініціювати з основної нитки, тому це не так, як exitваш єдиний варіант.
Барт ван Хекелом

@Bart: те, що ви описуєте, - це один із можливих способів вимкнення програми, а не правильний спосіб . Немає нічого поганого в використанні гачків відключення, щоб красиво зупинити всі теми. І запускаються гачки для відключення exit. Не єдиний варіант, але, безумовно, варіант, який варто розглянути.
Joonas Pulakaka

Але те, що я збираю з документів, гачки вимкнення дуже делікатні і можуть не мати багато часу, щоб зробити те, що ви хочете, щоб вони зробили. У будь-якому випадку, використовуйте їх для того, щоб вийти прекрасно у випадку відключення ОС чи чогось іншого, але якщо ви збираєтесь зателефонувати System.exit () самостійно, я думаю, що краще зробити код вимкнення перед цим (після чого, ймовірно, не вийде потрібен System.exit () більше
Барт ван Хекелом

1
Гачки для вимкнення мають весь час, який вони потребують, щоб закінчити. ВМ не зупиняється до того, як всі гачки повернуться. Дійсно можна запобігти виходу VM, зареєструвавши гак відключення, на виконання якого потрібно дуже багато часу. Але звичайно можна зробити послідовність відключення різними, альтернативними способами.
Joonas Pulakaka

Повернення з основного не вимкнеться, якщо є інші недемонові потоки.
djechlin

4

Специфікація мови Java говорить про це

Вихід з програми

Програма припиняє всю свою діяльність і припиняється, коли трапляється одна з двох речей:

Усі потоки, що не є демоновими нитками, закінчуються.

Деякий потік викликає метод виходу класу Runtime або класу System , і операція виходу не заборонена менеджером захисту.

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


3

Якщо в JVM працює інша програма і ви використовуєте System.exit, ця друга програма теж буде закрита. Уявімо, наприклад, що ви виконуєте завдання java на вузлі кластера і програма java, яка керує вузлом кластера, працює в тому ж JVM. Якщо завдання використовує System.exit, воно не тільки вийде з роботи, але й "вимкне повний вузол". Ви не змогли б надіслати інше завдання на цей вузол кластера, оскільки програма управління була закрита випадково.

Тому не використовуйте System.exit, якщо ви хочете мати можливість керувати своєю програмою з іншої програми java в межах того ж JVM.

Використовуйте System.exit, якщо ви хочете спеціально закрити повний JVM і якщо ви хочете скористатися можливостями, які були описані в інших відповідях (наприклад, вимкнути гаки: гак відключення Java , ненульове значення повернення для командного рядка виклики: як отримати статус виходу програми Java у пакетному файлі Windows ).

Також подивіться на Винятки під час виконання програми: System.exit (число) або викиньте RuntimeException з main?

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