Ответ 1
Давайте начнем с разных способов запуска последовательности выключения:
- Последний конец не-демона заканчивается.
- JVM прерывается (с помощью ctrl C или отправки SIGINT).
- JVM завершается (путем отправки SIGTERM)
- Один из потоков вызывает
System.exit()
илиRuntime.exit()
.
Когда вызывается System.exit(int)
, он вызывает Runtime.exit()
. Он проверяет с менеджером безопасности, разрешено ли ему выйти с данным статусом, и если да, вызовы Shutdown.exit()
.
Если вы прервали JVM или система отправила ему сигнал TERM, то по умолчанию Shutdown.exit()
вызывается непосредственно без проверки с менеджером безопасности.
Класс Shutdown
- это внутренний, пакетно-частный класс в java.lang
. Он имеет, среди прочего, методы exit()
и a halt()
. Его метод exit()
делает некоторые вещи, чтобы предотвратить выполнение крючков дважды и т.д., Но в основном, что он делает, это
- Запустите системные клики. Системные крючки регистрируются внутри методом JRE. Они запускаются последовательно, а не в потоках. Второй системный крючок - это то, что запускает крючки приложения, которые вы добавили. Он запускает каждый из них как нить, а затем имеет
join
для каждого из них в конце. Другие системные перехватчики могут запускаться до или после того, как приложение перехватывает. - Если финализаторы должны быть запущены до остановки, они запускаются. Обычно этого не происходит, поскольку метод устарел. И если выход имеет статус, отличный от нуля, он все равно игнорирует
runFinalizersOnExit
. - JVM остановлен.
Теперь, вопреки вашему предположению, на третьем этапе все потоки остановлены. Метод halt
является родным, и я не пытаюсь прочитать собственный код, но до момента его вызова единственный исполняемый код - это чистая Java, и ничто не останавливает нити в нем. В документации Runtime.addShutdownHook
говорится:
Крюк отключения - это просто инициализированный, но нерасширенный поток. Когда виртуальная машина начнет свою последовательность выключения, она запустит все зарегистрированные крючки отключения в некотором неуказанном порядке и позволит им запускать одновременно. Когда все крючки закончены, он будет запускать все неинвинированные финализаторы, если финализация на выходе будет включена. Наконец, виртуальная машина остановится. Обратите внимание, что потоки демона будут продолжать выполняться во время последовательности выключения, а также потоки не-демона, если выключение было инициировано вызовом метода exit.
(акцент мой)
Итак, вы видите, что на самом деле это часть задания на работу при завершении работы, чтобы сообщить потокам, что они должны оставить свои петли и очистить.
Другое неправильное представление о том, что вы придаете потоку высокий приоритет. Высокий приоритет не означает, что поток будет запускаться первым, перед всеми другими крючками. Это просто означает, что всякий раз, когда операционная система должна принять решение о том, какие из потоков, которые находятся в состоянии "готов к запуску", чтобы дать процессору работать, высокоприоритетный поток будет иметь более высокую вероятность "выигрыша" - в зависимости в алгоритме планирования операционной системы. Короче говоря, он может получить немного больше доступа к ЦП, но он не будет - особенно если у вас есть более одного ядра процессора - обязательно запускается перед другими потоками или завершается перед ними.
Последнее: если вы хотите использовать флаг, чтобы указать потоку прекратить работу, этот флаг должен быть volatile
.