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