Как правильно закрыть java ExecutorService
У меня есть простой java ExecutorService
, который запускает некоторые объекты задачи (реализует Callable
).
ExecutorService exec = Executors.newSingleThreadExecutor();
List<CallableTask> tasks = new ArrayList<>();
// ... create some tasks
for (CallableTask task : tasks) {
Future future = exec.submit(task);
result = (String) future.get(timeout, TimeUnit.SECONDS);
// TASKS load some classes and invoke their methods (they may create additional threads)
// ... catch interruptions and timeouts
}
exec.shutdownNow();
После завершения всех задач (DONE или TIMEOUT-ed) я пытаюсь выключить исполнитель, но он не остановится: exec.isTerminated() = FALSE.
Я подозреваю, что некоторые выполняемые задачи не завершены должным образом.
И да, я знаю, что выключение исполнителя не гарантирует ничего:
Нет никаких гарантий, кроме попыток прекращения усилий обработка, активно выполняющая задачи. Например, типичный реализация будет отменена с помощью {@link Thread # interrupt}, поэтому любые задача, которая не отвечает на прерывания, никогда не может завершиться.
Мой вопрос в том, есть ли способ гарантировать, что потоки (задачи) прекратятся?
Лучшее решение, которое я придумал, - это вызвать System.exit()
в конце моей программы, но это глупо.
Ответы
Ответ 1
Рекомендуемый способ на странице документации Oracle API ExecutorService:
void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
Если ваш пул занимает больше времени для выключения, вы можете изменить
1f (!pool.awaitTermination(60, TimeUnit.SECONDS))
to
while (!pool.awaitTermination(60, TimeUnit.SECONDS))
Краткий обзор связанных с выключением методов
shutdown():
Инициирует упорядоченное завершение работы, в котором выполняются ранее поставленные задачи, но новые задачи не будут приняты.
shutdownNow():
Попытка остановить все активное выполнение задач, приостанавливает обработку ожидающих задач и возвращает список задач, ожидающих выполнения.
waitTermination (длительный тайм-аут, единица TimeUnit) выбрасывает InterruptedException:
Блокирует до тех пор, пока все задачи не завершили выполнение после запроса на завершение работы или не произойдет тайм-аут, или текущий поток не будет прерван, в зависимости от того, что произойдет раньше.
Ответ 2
У вас есть контроль над этими задачами? т.е. вы сами создаете их? Я подозреваю, что в тех случаях, когда прерывание потока игнорируется, например.
try {
....
}
catch {InterruptedException e) {
// do nothing
}
При вызове InterruptedException флаг прерывания в потоке должен быть reset, иначе поток не выйдет. Подробнее см. .
К сожалению, вы можете использовать библиотеку, которая не подчиняется этому, и в этом случае вы не можете легко обойти это. В этом случае одна опция супертяга состоит в том, чтобы разблокировать вспомогательный процесс для выполнения задания Callable
, и это очистит все ресурсы после выхода из процесса. Супертяжелый вес и, возможно, нетривиальный, но надежный.