Статьи

Знайте серию JVM: крюки отключения

В этой статье серии «Знай JVM» мы рассмотрим еще одну функцию, предлагаемую JVM, которая не очень известна. Завершение работы — это специальная конструкция, которая позволяет разработчикам подключать фрагмент кода, который будет выполняться при завершении работы JVM. Это удобно в тех случаях, когда нам необходимо выполнить специальные операции очистки в случае выключения виртуальной машины.

Обработка этого с использованием общих конструкций, таких как обеспечение того, чтобы мы вызывали специальную процедуру до того, как приложение существует (вызов System.exit (0)), не будет работать в ситуациях, когда виртуальная машина выключается по внешней причине (например, запрос на удаление). из O / S), или из-за проблемы с ресурсами (нехватка памяти). Как мы скоро увидим, перехватчики завершения легко решают эту проблему, позволяя вам предоставить произвольный кодовый блок, который будет вызываться JVM при его завершении.

С поверхности, используя крюк отключения прямо прямо вперед. Все, что вам нужно сделать, это просто написать класс, который расширяет класс java.lang.Thread и обеспечивает логику, которую вы хотите выполнить при завершении работы виртуальной машины, внутри публичного метода void run (). Затем вы регистрируете экземпляр этого класса в качестве перехватчика завершения работы виртуальной машины, вызывая метод Runtime.getRuntime (). AddShutdownHook (Thread). Если вам необходимо удалить ранее зарегистрированный обработчик завершения работы, класс Runtime также предоставляет метод removeShutdownHook (Thread).

Например,

public class TestHook {    public static void main(String[] args) {        Runtime.getRuntime().addShutdownHook(new Thread() {            public void run() {                System.out.println("Shutdown Hook is running !");            }        });        System.out.println("Application Terminating ...");    }}

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

Application Terminating ...Shutdown Hook is running !

Просто верно? Да, это.

Таким образом, у вас возникнет вопрос: «Какова цель написания статьи, чтобы продемонстрировать что-то настолько простое, что могло бы легко вписаться в ограничение в 250 символов твита?»; Ну, ответ в том, что история на этом не заканчивается.

 

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

1. В некоторых случаях крюки выключения могут не выполняться!

Первое, что нужно иметь в виду, это то, что не гарантируется, что крюки отключения будут работать всегда. Если JVM падает из-за какой-то внутренней ошибки, он может потерпеть крах, не имея возможности выполнить одну инструкцию. Кроме того, если O / S подает сигнал SIGKILL (http://en.wikipedia.org/wiki/SIGKILL) (kill -9 в Unix / Linux) или TerminateProcess (Windows), то приложение должно немедленно завершиться без делать даже в ожидании каких-либо действий по уборке. В дополнение к вышесказанному также возможно завершить работу JVM, не позволяя запускать обработчики завершения работы, вызвав метод Runime.halt ().

Хуки завершения вызываются, когда приложение нормально завершается (когда завершаются все потоки или когда вызывается System.exit (0)). Кроме того, когда JVM завершает работу из-за внешних причин, таких как пользователь, запрашивающий завершение (Ctrl + C), SIGTERM выдается O / S (обычная команда kill, без -9), или когда операционная система выключается ,

2. После запуска крюки выключения можно принудительно остановить до завершения.

На самом деле это частный случай описанного выше случая. Хотя ловушка начинает выполнение, ее можно прервать до ее завершения в таких случаях, как завершение работы операционной системы. В таких случаях O / S ожидает завершения процесса в течение заданного промежутка времени после получения SIGTERM. Если процесс не завершается в течение этого времени, то O / S принудительно завершает процесс, выдавая SIGTERM (или аналоги в Windows). Так что возможно, что это происходит, когда перехватчик завершает свою работу на полпути.

Следовательно, рекомендуется следить за тем, чтобы крюки отключения были написаны с осторожностью, чтобы они быстро заканчивались и не вызывали таких ситуаций, как взаимоблокировки. Кроме того, JavaDoc [1] специально упоминает, что не следует выполнять длинные вычисления или ждать операций ввода-вывода пользователя в ловушке завершения работы.

3. Вы можете иметь более одного крюка отключения, но порядок их выполнения не гарантируется.

Как вы, возможно, правильно догадались по имени метода метода addShutdownHook (вместо setShutdownHook), вы можете зарегистрировать несколько хуков завершения работы. Но порядок выполнения этих нескольких перехватчиков не гарантируется JVM. JVM может выполнять перехватчики отключения в любом произвольном порядке. Кроме того, JVM может выполнять все эти ловушки одновременно.

 

4. Вы не можете зарегистрировать / отменить регистрацию блокировок при отключении

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

 

5. После запуска последовательности выключения она может быть остановлена ​​только Runtime.halt ().

Как только начинается последовательность выключения, только Runtime.halt () (которая принудительно завершает JVM) может остановить выполнение последовательности выключения (за исключением внешних воздействий, таких как SIGKILL). Это означает, что вызов System.exit () с помощью Shutdown Hook не будет работать. На самом деле, если вы вызовете System.exit () с помощью Shutdown Hook, виртуальная машина застрянет, и вам придется принудительно завершить процесс.

 

6. Использование перехватчиков требует разрешения безопасности.

Если вы используете менеджеры безопасности Java, то код, который выполняет добавление / удаление перехватчиков завершения работы, должен иметь разрешение shutdownHooks во время выполнения. Если вы вызываете этот метод без разрешения в защищенной среде, то это приведет к SecurityException.

 

7. Исключения, сгенерированные крюками выключения, обрабатываются так же, как и исключения, сгенерированные любым другим сегментом кода.

Таким образом, здесь применяется стандартный механизм распространения исключений, и для неперехваченных исключений будут вызываться обработчики неперехваченных исключений по умолчанию. Подробное обсуждение обработчиков необработанных исключений см. В статье 1 «Знай серию JVM — обработчик необработанных исключений» .

 

Более подробные сведения о проектных решениях API-интерфейсов Java Shutdown Hooks описаны в статье «Разработка API-интерфейсов Shutdown Hooks» [2] . Он предоставляет обоснование для использования реализаций класса Thread вместо интерфейса Runnable и многих других решений, принимаемых разработчиками API.

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

В заключение, API-интерфейсы Shutdown Hooks предоставляют простой способ добавить произвольный код в последовательность завершения работы JVM. Несмотря на то, что API может показаться простым, следует тщательно рассмотреть последствия и последствия действий, предпринятых хуком отключения, прежде чем писать его.

Ссылки

[1] JavaDoc для JDK 1.5 — Runtime.addShutdownHook

[2] Дизайн API Shutdown Hooks

Посмотреть оригинальный пост и похожие посты в моем блоге .