Программа не прекращается сразу же после выполнения всех задач ExecutorService
Я помещаю кучу запущенных объектов в ExecutorService:
// simplified content of main method
ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i = 0; i < workerCount; i++) {
threadPool.execute(new Worker());
}
Я ожидаю, что моя программа/процесс остановится сразу после того, как все работники будут выполнены. Но согласно моему журналу, это займет еще 20-30 секунд, пока это не произойдет. Работники не выделяют никаких ресурсов, фактически они ничего не делают в данный момент.
Не поймите меня неправильно, это не очень важная проблема для меня, я просто пытаюсь понять, что происходит, и мне интересно, нормальное поведение.
Ответы
Ответ 1
Executors.newCachedThreadPool()
использует Executors.defaultThreadFactory()
для своего ThreadFactory
. defaultThreadFactory
javadocs говорят, что "каждый новый поток создается как поток non-daemon" (выделено мной). Таким образом, потоки, созданные для newCachedThreadPool
, являются не-демонами. Это означает, что они будут препятствовать естественному выходу JVM ( "естественно", я имею в виду, что вы все равно можете вызвать System.exit(1)
или убить программу, чтобы заставить JVM остановиться).
Причина, по которой приложение заканчивается, заключается в том, что каждый поток, созданный в newCachedThreadPool
, истекает и закрывается после некоторого времени бездействия. Когда последний из них закрывается, если ваше приложение не имеет ни одного не-демона, он будет закрыт.
Вы можете (и должны) закрыть ExecutorService
вниз вручную через shutdown
или shutdownNow
.
См. также JavaDoc для Thread, в котором говорится о демонах.
Ответ 2
Я ожидаю, что моя программа/процесс остановится сразу после того, как все работники будут выполнены. Но согласно моему журналу, это займет еще 20-30 секунд, пока это не произойдет. Работники не выделяют никаких ресурсов, фактически они ничего не делают в данный момент.
Проблема заключается в том, что вы не закрываете свой ExecutorService
. После того, как вы отправите все задания в службу, вы должны отключить службу, или JVM не будет завершаться, если все потоки в ней не являются потоками демона. Если вы не отключите пул потоков, то любые потоки, связанные с ExecutorService
, опять же, если не демон, остановят JVM от завершения. Если вы отправили какие-либо задачи в кешированный пул потоков, вам придется подождать, пока потоки перейдут на тайм-аут и получат прибыль до завершения JVM.
ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i = 0; i < workerCount; i++) {
threadPool.execute(new Worker());
}
// you _must_ do this
threadPool.shutdown();
Запуск потоков как демона скорее всего не то, что вы хотите сделать, потому что ваше приложение может остановиться до завершения задач, и все задачи будут немедленно прекращены в это время. Из 178 раз мы используем классы ExecutorService
в нашем производственном коде, только 2 из них были запущены как потоки демона. Остальные нормально завершены.
Если вам нужно принудительно остановить ExecutorService
, когда приложение выходит, а затем использовать shutdownNow()
с правильной обработкой флагов прерывания потока в порядке.
Ответ 3
Из javadoc для Executors.newCachedThreadPool()
:
Нити, которые не использовались в течение шестидесяти секунд, завершаются и удаляются из кеша.
Обычно рекомендуется называть shutdown()
на ExecutorService
, если вы знаете, что никакие новые задачи не будут отправлены на него. Затем все задачи в очереди будут завершены, но служба немедленно отключится.
(В качестве альтернативы, если вам все равно, выполняются ли все задачи, например, если они обрабатывают фоновые вычисления, которые не имеют значения после того, как ваш основной интерфейс исчезнут, тогда вы можете создать ThreadFactory
, который устанавливает все потоки в этом пуле будет демон.)
Ответ 4
В основном на ExecutorService вы вызываете shutdown(), а затем ожидаетеTermination():
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
taskExecutor.execute(new MyTask());
}
taskExecutor.shutdown();
try {
taskExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
...
}
Ответ 5
Для многопоточной обработки ExecutorService
Решение
threadPool.shutdown();