Что произойдет, если System.exit вызывается из ловушки завершения работы?

У меня довольно сложное завершение работы в Java - предстоит много работы по очистке. В частности, я пытаюсь выяснить, как обрабатывать ошибки из потока крюка отключения. Мой код включает это в настоящее время:

try {
    return shutdownPromise = doShutdown();
}
catch (Throwable exc) {
    logger.error("An exception was thrown while shutting down the application.", exc);  
    System.exit(1);
    return null;
}

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

Так что я подумал -

  1. Что делает вызывающий выход из ловушки отключения?
  2. Как правильно обращаться с ошибкой из хука отключения?

1 ответ

Решение

Во-первых, простой ответ: ваш процесс заходит в тупик.

System.exit завершает вызов Shutdown.exit, который заканчивается этим блоком кода:

synchronized (Shutdown.class) {
     /* Synchronize on the class object, causing any other thread
      * that attempts to initiate shutdown to stall indefinitely
      */
     sequence();
     halt(status);
}

Комментарий абсолютно точный. Но чтобы понять это, мы должны точно знать, что делает крюк отключения.

Крюки отключения добавляются через ApplicationShutdownHooks.addКак объясняется в этом ответе здесь. Обратите внимание, что sequence Метод выше в конечном итоге оказывается в этой строке кода в Shutdown.runHooks:

if (hook != null) hook.run();

Во-первых, обратите внимание, что эти перехватчики не являются перехватчиками завершения работы вашего приложения. Они являются системными перехватчиками, которые отличаются друг от друга, и один из них отвечает за обеспечение работы перехватчиков завершения работы вашего приложения. Обратите внимание, что это запуск, а не запуск. Другими словами, это блокирует. Одним из этих перехватчиков завершения работы будет системный перехватчик, который одновременно запускает все перехваты завершения работы приложения (вы можете отследить этот код для себя или в связанном ответе), а затем блокирует, пока все они не завершат работу, используя Thread.join вызов. Одним из них будет хук завершения работы вашего приложения - который, если он вызывает System.exitбудет ждать блокировки Shutdown.class навсегда.

Далее тупой ответ: звоните Runtime.getRuntime().halt() вместо. Я говорю "тупой ответ", потому что это, вероятно, немного более низкий уровень, который вы хотели сделать вместо выхода. Если System.exit представляет собой чистое отключение, которое не удалось, то halt это жесткое отключение, которое всегда будет успешным сразу.

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

Другие вопросы по тегам