«1. Обзор

В этом руководстве мы рассмотрим System.exit(), Runtime.getRuntime().halt() и сравнение этих двух методов друг с другом.

2. System.exit()

Метод System.exit() останавливает работающую виртуальную машину Java. Но перед остановкой JVM вызывается последовательность выключения, также известная как упорядоченное выключение. Пожалуйста, обратитесь к этой статье, чтобы узнать больше о добавлении перехватчиков выключения.

Последовательность выключения JVM сначала вызывает все зарегистрированные перехватчики выключения и ожидает их завершения. Затем он запускает все незадействованные финализаторы, если включена финализация при выходе. Наконец, он останавливает JVM.

Этот метод фактически вызывает метод Runtime.getRuntime().exit() внутри. Он принимает целочисленный код состояния в качестве аргумента и имеет тип возвращаемого значения void:

public static void exit(int status)

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

3. Runtime.getRuntime().halt()

Класс Runtime позволяет приложению взаимодействовать со средой, в которой оно работает.

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

В отличие от метода выхода, этот метод не запускает последовательность завершения работы JVM. Поэтому ни перехватчики выключения, ни финализаторы не выполняются, когда мы вызываем метод halt.

Этот метод не является статическим и имеет сигнатуру, аналогичную System.exit():

public void halt(int status)

Подобно выходу, ненулевой код состояния в этом методе также указывает на аварийное завершение программы.

4. Пример

Теперь давайте рассмотрим пример методов выхода и остановки с помощью перехватчика выключения.

Для простоты мы создадим класс Java и зарегистрируем перехватчик выключения в статическом блоке. Также мы создадим два метода; первый вызывает метод выхода, а второй вызывает метод остановки:

public class JvmExitAndHaltDemo {

    private static Logger LOGGER = LoggerFactory.getLogger(JvmExitAndHaltDemo.class);

    static {
        Runtime.getRuntime()
          .addShutdownHook(new Thread(() -> {
            LOGGER.info("Shutdown hook initiated.");
          }));
    }

    public void processAndExit() {
        process();
        LOGGER.info("Calling System.exit().");
        System.exit(0);
    }

    public void processAndHalt() {
        process();
        LOGGER.info("Calling Runtime.getRuntime().halt().");
        Runtime.getRuntime().halt(0);
    }

    private void process() {
        LOGGER.info("Process started.");
    }

}

Итак, чтобы сначала протестировать метод выхода, давайте создадим тестовый пример:

@Test
public void givenProcessComplete_whenExitCalled_thenTriggerShutdownHook() {
    jvmExitAndHaltDemo.processAndExit();
}

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

12:48:43.156 [main] INFO com.baeldung.exitvshalt.JvmExitAndHaltDemo - Process started.
12:48:43.159 [main] INFO com.baeldung.exitvshalt.JvmExitAndHaltDemo - Calling System.exit().
12:48:43.160 [Thread-0] INFO com.baeldung.exitvshalt.JvmExitAndHaltDemo - Shutdown hook initiated.

Точно так же мы создадим тестовый пример для метода halt:

@Test
public void givenProcessComplete_whenHaltCalled_thenDoNotTriggerShutdownHook() {
    jvmExitAndHaltDemo.processAndHalt();
}

Теперь мы можем запустить этот тестовый пример и убедиться, что хук выключения не вызывается :

12:49:16.839 [main] INFO com.baeldung.exitvshalt.JvmExitAndHaltDemo - Process started.
12:49:16.842 [main] INFO com.baeldung.exitvshalt.JvmExitAndHaltDemo - Calling Runtime.getRuntime().halt().

5. Когда использовать exit и halt

Как мы видели ранее, метод System.exit() запускает последовательность завершения работы JVM, тогда как метод Runtime.getRuntime().halt() резко завершает работу JVM.

Мы также можем сделать это с помощью команд операционной системы. Например, мы можем использовать SIGINT или Ctrl+C, чтобы инициировать упорядоченное завершение работы, такое как System.exit() и SIGKILL, чтобы внезапно завершить процесс JVM.

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

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

Наконец, приложение также может ограничить случайное использование этих методов. Оба эти метода вызывают метод checkExit класса SecurityManager. Таким образом, чтобы запретить операции выхода и остановки, приложение может создать политику безопасности с помощью класса SecurityManager и сгенерировать исключение SecurityException из метода checkExit.

6. Заключение

В этом руководстве мы рассмотрели методы System.exit() и Runtime.getRuntime().halt() на примере. Кроме того, мы также говорили об использовании и лучших практиках этих методов.

Как обычно, полный исходный код этой статьи доступен на Github.