закрыть крюк
Java предоставляет механизм Shutdown Hook, который позволяет нам выполнять некоторую работу по очистке, когда программа завершается нормально или когда возникает исключение. Используемый метод также очень прост,Java.Runtime.addShutdownHook(Thread hook)
Вот и все. Перехватчик выключения фактически можно рассматривать как поток, который был инициализирован, но еще не запущен.Когда JVM выключается, все зарегистрированные перехватчики выключения будут выполняться одновременно.
Время выполнения хука
Когда вызовется хук выключения после регистрации в JVM, а когда не будет вызываться? в следующие случаи:
- Он будет вызван, когда программа Java завершит свою работу в обычном режиме и завершит работу.
- Вызывается, когда команда завершается через ctrl-c как в терминалах Windows, так и в Linux.
- Он будет вызываться, когда JVM завершает работу с OutOfMemory.
- Он вызывается, когда System.exit() выполняется в программе Java.
- Вызывается при завершении работы операционной системы.
- Вызывается, когда linux убивает процесс с kill pid (кроме kill -9 pid).
- Windows не будет вызвана, когда она завершит процесс напрямую.
добавить хук удаления
Добавление и удаление хуков реализовано через Runtime, а реализация внутри относительно проста.Вы видите, что методы addShutdownHook и removeShutdownHook сначала проверяют наличие разрешения shutdownHooks через менеджера безопасности, а затем добавляют и удаляют хуки через ApplicationShutdownHooks.
public void addShutdownHook(Thread hook) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("shutdownHooks"));
}
ApplicationShutdownHooks.add(hook);
}
public boolean removeShutdownHook(Thread hook) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("shutdownHooks"));
}
return ApplicationShutdownHooks.remove(hook);
}
ApplicationShutdownHooks Крючки для хранения
ApplicationShutdownHooks можно рассматривать как контейнер, используемый для хранения всех обработчиков завершения работы, и в основном обработчики хранятся через переменную типа IdentityHashMap.
private static IdentityHashMap<Thread, Thread> hooks;
С переменными хуками добавление и удаление хуков — это непосредственное выполнение операций размещения и удаления на этой HashMap, и перед операцией будут выполняться некоторые проверки, например, три решения перед добавлением хуков:
- Все ли хуки начали выполняться, если хуки равны null, это означает, что все хуки выключения начали выполняться и не могут быть добавлены в данный момент.
- Жив ли статус хука, если есть, значит хук уже запущен и его нельзя добавить.
- Если хук уже включен, его нельзя добавить, если он уже включен.
Аналогичная логика суждения также имеет операцию удаления.
static synchronized void add(Thread hook) {
if(hooks == null)
throw new IllegalStateException("Shutdown in progress");
if (hook.isAlive())
throw new IllegalArgumentException("Hook already running");
if (hooks.containsKey(hook))
throw new IllegalArgumentException("Hook previously registered");
hooks.put(hook, hook);
}
static synchronized boolean remove(Thread hook) {
if(hooks == null)
throw new IllegalStateException("Shutdown in progress");
if (hook == null)
throw new NullPointerException();
return hooks.remove(hook) != null;
}
Задача в ApplicationShutdownHooks, которая реально отвечает за запуск всех хуков, обрабатывается методом runHooks, логика его следующая:
- Сначала заблокируйте класс ApplicationShutdownHooks и получите все ловушки, а затем установите для переменной ловушек значение null .
- Обходим все хуки и запускаем хуки соответственно. Как упоминалось ранее, закрытие хука фактически можно рассматривать как поток, который был инициализирован, но еще не запущен. Здесь вы можете вызвать метод start, чтобы запустить его.
-
Используйте метод соединения для координации всех потоков ловушек и дождитесь их завершения.
static void runHooks() { Collection<Thread> threads; synchronized(ApplicationShutdownHooks.class) { threads = hooks.keySet(); hooks = null; } for (Thread hook : threads) { hook.start(); } for (Thread hook : threads) { try { hook.join(); } catch (InterruptedException x) { } } }
Кто отвечает за вызов метода runHooks ApplicationShutdownHooks? Таким образом, на самом деле это объект Runnable, добавленный в класс Shutdown, а метод run класса Runnable отвечает за вызов метода runHooks. Следующий шаг — увидеть, когда класс Shutdown выполняет объект Runnable.
Shutdown.add(1 , false ,
new Runnable() {
public void run() {
runHooks();
}
}
);
Хуки в выключенном состоянии
Логика добавления Runnable объекта ApplicationShutdownHooks в Shutdown следующая:
private static final int RUNNING = 0;
private static final int HOOKS = 1;
private static final int FINALIZERS = 2;
private static final int MAX_SYSTEM_HOOKS = 10;
private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];
static void add(int slot, boolean registerShutdownInProgress, Runnable hook) {
synchronized (lock) {
if (hooks[slot] != null)
throw new InternalError("Shutdown hook at slot " + slot + " already registered");
if (!registerShutdownInProgress) {
if (state > RUNNING)
throw new IllegalStateException("Shutdown in progress");
} else {
if (state > HOOKS || (state == HOOKS && slot <= currentRunningHook))
throw new IllegalStateException("Shutdown in progress");
}
hooks[slot] = hook;
}
}
Слот указывает, какому элементу в массиве ловушек назначить объект Runnable.Shutdown также имеет переменную ловушек, которая имеет тип Runnable[] и имеет длину MAX_SYSTEM_HOOKS, которая равна 10. Этот массив можно рассматривать как приоритетную реализацию хука, нижний индекс массива используется для обозначения приоритета, а slot = 1 означает присваивание второму элементу в массиве.
registerShutdownInProgress указывает, разрешать ли регистрацию ловушек, даже если выполняется завершение работы. Очевидно, что передача false впереди запрещена. Состояние > RUNNING означает, что все остальные состояния будут генерировать исключение, если только это не состояние RUNNING. Если registerShutdownInProgress имеет значение true, пока он не находится в состоянии FINALIZERS, а слот должен быть больше нижнего индекса текущего массива ловушек.
В случае времени выполнения хука, упомянутого выше, JVM вызовет метод последовательности класса Shutdown следующим образом:
private static void sequence() {
synchronized (lock) {
if (state != HOOKS) return;
}
runHooks();
boolean rfoe;
synchronized (lock) {
state = FINALIZERS;
rfoe = runFinalizersOnExit;
}
if (rfoe) runAllFinalizers();
}
private static void runHooks() {
for (int i=0; i < MAX_SYSTEM_HOOKS; i++) {
try {
Runnable hook;
synchronized (lock) {
currentRunningHook = i;
hook = hooks[i];
}
if (hook != null) hook.run();
} catch(Throwable t) {
if (t instanceof ThreadDeath) {
ThreadDeath td = (ThreadDeath)t;
throw td;
}
}
}
}
Во-первых, если текущее состояние не равно HOOKS, оно вернется напрямую, а затем выполнит метод runHooks, который также является методом, который мы в основном рассматриваем. Затем установите состояние FINALIZERS и, наконец, вызовите метод runAllFinalizers, чтобы при необходимости выполнить все финализаторы. Таким образом, метод runHooks будет вызываться при завершении работы JVM.
Логика метода runHooks проста: он проходит по массиву Runnable и один за другим вызывает свой метод run для его выполнения.
Ниже приведенырекламироватьа такжеСвязанное Чтение
========Время рекламы========
Моя новая книга «Анализ дизайна ядра Tomcat» продана на Jingdong, нуждающиеся друзья могут обратиться кitem.JD.com/12185360.Контракт…Зарезервировать. Спасибо друзья.
Зачем писать «Анализ проектирования ядра Tomcat»
=========================
Связанное чтение:
Объект с точки зрения исходного кода JDK
Долго с точки зрения исходного кода JDK
Целое число с точки зрения исходного кода JDK
Плавающие с точки зрения исходного кода JDK
Достаточно ли изменчив, чтобы гарантировать синхронизацию данных?
Разговор об основных типах данных Java
Оптимизация одновременных блокировок с точки зрения исходного кода JDK
Блокировка и пробуждение потоков с точки зрения исходного кода JDK
С точки зрения исходного кода JDK тайм-аут параллельной конкуренции
Прерывание параллельных потоков Java с точки зрения исходного кода JDK
Справедливость параллелизма Java с точки зрения исходного кода JDK
Как обеспечить атомарность параллелизма Java с точки зрения исходного кода JDK
Байт с точки зрения исходного кода JDK
Логическое значение с точки зрения исходного кода JDK
Кратко с точки зрения исходного кода JDK
См. System.exit из исходного кода JDK.
Добро пожаловать, чтобы следовать: