Почему интерфейс ExecutorService не реализует AutoCloseable?
Сбой при вызове shutdown()
для исполнителя потока приведет к тому, что приложение никогда не завершится.
Лучшая практика для закрытия ExecutorService заключается в следующем:
ExecutorService service = null;
try {
service = Executors.newSingleThreadExecutor();
// add tasks to thread executor
…
} finally {
if (service != null) service.shutdown();
}
Поскольку Java знает концепцию "попробуй с ресурсами", было бы неплохо, если бы мы могли это сделать?
try (service = Executors.newSingleThreadExecutor())
{
// add tasks to thread executor
…
}
Ответы
Ответ 1
Что ExecutorService имеет фактически два действия, связанные с выключением; основанный на простом факте, что оба способы закрытия службы имеют смысл.
Таким образом: как бы вы автоматически закрыли сервис? В последовательном порядке, который работает для всех?!
Итак, разумное объяснение в моих глазах: вы не можете сделать ExecutorService автоматической блокировкой, потому что у этой службы нет единой операции закрытия; но два!
И если вы считаете, что можете использовать такое автоматическое закрытие, запись вашей собственной реализации с использованием "делегирования" будет за 5 минут! Или, вероятно, 10 минут, потому что вы создадите одну версию, вызывающую shutdown()
как закрытую операцию; и тот, который делает shutdownNow()
вместо этого.
Ответ 2
Это посредственный обходной путь
ExecutorService service = Executors.newSingleThreadExecutor();
try (Closeable close = service::shutdown) {
}
Или, если проверенное исключение беспокоит вас, вы можете написать:
interface MyCloseable extends AutoCloseable {
void close();
}
А потом
ExecutorService service = Executors.newSingleThreadExecutor();
try (MyCloseable close = service::shutdown) {
}
Конечно, вы никогда не должны помещать что-либо между присваиванием и оператором try
и не использовать локальную переменную service
после оператора try
.
Учитывая предостережения, просто используйте, finally
вместо этого.
Ответ 3
Я не вижу, что AutoCloseable является полезным для Исполнителя. try-with-resources - это вещи, которые могут быть инициализированы, использованы и выпущены в рамках метода. Это отлично подходит для таких вещей, как файлы, сетевые подключения, ресурсы jdbc и т.д., Где они быстро открываются, используются и быстро очищаются. Но исполнитель, особенно threadpool, - это то, что вы хотите использовать в течение длительного периода времени, возможно, в течение всего срока службы приложения и, как правило, впрыскивается в такие вещи, как singleton-сервисы, которые могут иметь метод, который инфраструктура DI знает вызовите выключение приложения, чтобы очистить исполнитель. Эта схема использования отлично работает без использования ресурсов try-with.
Кроме того, большой мотиватор, стоящий за попытками использования ресурсов, заключается в том, что исключения не маскируются. Это не так много для исполнителей, все бросание исключений будет происходить в задачах, переданных исполнителю,
маскирование исключений не является проблемой.
Ответ 4
Try-with-resources - это автоматическое закрытие Readers/Streams, где ExecutorService - это выполнение задач с использованием пула потоков.
Таким образом, я не уверен, существует ли параллель между двумя, что мы можем думать о применении try-with-resources к ExecutorServices.
ОБНОВЛЕНО
Цитирование спецификации языка Java:
Оператор try-with-resources параметризуется переменными (называемыми ресурсами), которые инициализируются перед исполнением блока try и автоматически закрываются в обратном порядке, из которого они были инициализированы, после выполнения блока try. catch и предложение finally часто не нужны, когда ресурсы автоматически закрываются.
Спектр вызывает переменные как "ресурсы". Поэтому я не уверен, что ExecutorService можно назвать ресурсом, и поэтому не думайте, что ExecutorService как параллель с Readers/Streams/Statement/ResultSet/Connection и т.д.