JavaRush /Курсы /Модуль 2. Java Core /Остановка ExecutorService

Остановка ExecutorService

Модуль 2. Java Core
13 уровень , 5 лекция
Открыта

Рассмотрим простейшую программу:


public static void main(String[] args) throws Exception {
	// создаем ExecutorService с фиксированным числом нитей – три
	ExecutorService service = Executors.newFixedThreadPool(3);
 
	// передаем в ExecutorService простое задание типа Runnable
	service.submit(() -> System.out.println("done"));
}

Запустив программу, получаем в консоли ожидаемый вывод:

done

Но далее не видим привычного для IntellijIDEA вывода:

Process finished with exit code 0

Его обычно можно увидеть в конце выполнения программы.

Почему так происходит?

Из описания метода newFixedThreadPool() можем узнать, что нити, созданные с помощью этого ExecutorService, продолжат существовать до их явной остановки. Следовательно, раз мы передали в ExecutorService одно задание, то для его выполнения была создана нить, которая осталась существовать и после выполнения задания.

Остановка ExecutorService

Итак, ExecutorService нужно за собой “закрывать” (останавливать). Сделать это можно двумя способами:

  1. void shutdown() — после вызова этого метода ExecutorService больше не принимает новые задания. Все задания, которые раннее были переданы в ExecutorService, продолжат свое выполнение.

    
    public static void main(String[] args) throws Exception {
    ExecutorService service = Executors.newFixedThreadPool(3);
        	service.submit(() -> System.out.println("task 1"));
        	service.submit(() -> System.out.println("task 2"));
        	service.shutdown();
        	// здесь произойдет RejectedExecutionException
        	service.submit(() -> System.out.println("task 3"));
    }
    
  2. List<Runnable> shutdownNow() — метод пытается остановить текущие активные задания. Задачи, которые ждали своей очереди, отбрасываются и возвращаются в виде списка Runnable.

    
    public static void main(String[] args) throws Exception {
        ExecutorService service = Executors.newFixedThreadPool(5);
        List.of(1, 2, 3, 4, 5, 6, 7, 8).forEach(i -> service.submit(() -> System.out.println(i)));
        List<Runnable> runnables = service.shutdownNow();
        runnables.forEach(System.out::println);
    }
    

Вывод программы:

1
2
4
3
java.util.concurrent.FutureTask@1e80bfe8[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@4edde6e5[Wrapped task = Test$$Lambda$16/0x0000000800b95040@70177ecd]]
java.util.concurrent.FutureTask@cc34f4d[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@66a29884[Wrapped task = Test$$Lambda$16/0x0000000800b95040@4769b07b]]
java.util.concurrent.FutureTask@6f539caf[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@17a7cec2[Wrapped task = Test$$Lambda$16/0x0000000800b95040@65b3120a]]
5

Process finished with exit code 0

Вывод будет отличаться от запуска к запуску. В выводе есть 2 вида строк:

  • число — это значит, что эта задача успела обработаться ExecutorService, и вывелось число из списка, из которого мы создавали задачи.

  • объект типа FutureTask, после вызова у него метода toString(). Это задачи, которые были переданы ExecutorService на выполнение, но не были обработаны.

В этом выводе есть еще один интересный нюанс. Если бы мы жили в идеальном мире, то сначала вывелись бы все числа, а потом объекты типа FutureTask. Но из-за проблем синхронизации строки в выводе перемешаны.

Другие методы

Кроме этого у ExecutorService есть еще несколько методов, связанных с его остановкой:

  1. boolean awaitTermination(long timeout, TimeUnit unit) — метод блокирует нить, которая его вызвала. Блокировка прерывается, как только наступает любое из трех событий:

    • после вызова метода shutdown() все активные задания и все задания из очереди были выполнены;
    • закончился таймаут, длительность которого определяется параметрами метода;
    • нить, вызвавшая метод awaitTermination(), была прервана.

    Метод возвращает true, если ExecutorService был остановлен до истечения таймаута, и false, если таймаут истек раньше.

    
    public static void main(String[] args) throws Exception {
    	ExecutorService service = Executors.newFixedThreadPool(2);
    	service.submit(() -> System.out.println("task 1"));
    	service.submit(() -> System.out.println("task 2"));
    	service.submit(() -> System.out.println("task 3"));
    	service.shutdown();
    	System.out.println(service.awaitTermination(1, TimeUnit.MICROSECONDS));
    }
    
  2. boolean isShutdown() — возвращает true, если у ExecutorService был вызван метод shutdown() или shutdownNow().

    
    public static void main(String[] args) throws Exception {
    	ExecutorService service = Executors.newFixedThreadPool(2);
    	service.submit(() -> System.out.println("task 1"));
    	service.submit(() -> System.out.println("task 2"));
    	service.submit(() -> System.out.println("task 3"));
    	System.out.println(service.isShutdown());
    	service.shutdown();
    	System.out.println(service.isShutdown());
    }
    
  3. boolean isTerminated() — возвращает true, если у ExecutorService был вызван метод shutdown() или shutdownNow() и завершено выполнение всех заданий.

    
    public static void main(String[] args) throws Exception {
        ExecutorService service = Executors.newFixedThreadPool(5);
        List.of(1, 2, 3, 4, 5, 6, 7, 8).forEach(i -> service.submit(() -> System.out.println(i)));
        service.shutdownNow();
        System.out.println(service.isTerminated());
    }
    

Пример кода с использованием рассмотренных методов:


public static void main(String[] args) throws Exception {
   ExecutorService service = Executors.newFixedThreadPool(16);
   Callable<String> task = () -> {
       Thread.sleep(1);
       return "Done";
   };
 
   // добавляем в очередь на выполнение 10 тыс. заданий
   List<Future<String>> futures = IntStream.range(0, 10_000)
           .mapToObj(i -> service.submit(task))
           .collect(Collectors.toList());
   System.out.printf("На выполнение отправлено %d заданий.%n", futures.size());
 
   // пробуем закрыть
   service.shutdown();
   // ждем окончания работы 100 миллисекунд
   if (service.awaitTermination(100, TimeUnit.MILLISECONDS)) {
       System.out.println("Все задания выполнены!");
   } else {
       // принудительно останавливаем
       List<Runnable> notExecuted = service.shutdownNow();
       System.out.printf("Так и не запустилось %d заданий.%n", notExecuted.size());
   }
 
   System.out.printf("Всего выполнено %d заданий.%n", futures.stream().filter(Future::isDone).count());
}

Вывод программы (отличается от запуска к запуску):

На выполнение отправлено 10000 заданий.
Так и не запустилось 9170 заданий.
Всего выполнено 830 заданий.

Process finished with exit code 0
Комментарии (19)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Samandar Уровень 62
6 декабря 2024
Люди просто верьте в себя, не надейтесь только на эту платформу, ищите в гугле и в ютубе потом поймете, нойте но потом разберитесь полностью
Олег Уровень 111 Expert
4 октября 2024
Остановка ExecutorService (Краткая памятка): 1. Методы остановки: - shutdown(): Инициирует плавную остановку. Новые задачи не принимаются, но текущие выполняются до завершения. - shutdownNow(): Принудительная остановка. Попытается остановить все активные задачи и вернет список задач, которые не были выполнены. 2. Проверка состояния: - isShutdown(): Возвращает `true`, если вызван метод `shutdown()` или `shutdownNow()`. - isTerminated(): Возвращает `true`, если все задачи завершены после вызова `shutdown()` или `shutdownNow()`. 3. Ожидание завершения: - awaitTermination(long timeout, TimeUnit unit): Блокирует текущий поток до завершения всех задач или истечения таймаута. Возвращает `true`, если все задачи завершены до истечения таймаута. 4. Примечание: - После вызова `shutdown()` задачи в состоянии ожидания не будут запущены. - Используйте `shutdownNow()`, если требуется немедленная остановка, но это может привести к неполной обработке задач.
Дмитрий Уровень 49 Expert
20 июля 2024
радует одно, что судя по комментам, люди, которые сейчас на 100+ лвл и кажутся какими-то мега человеками тоже когда-то ныли в комментах, что ничего не понятно и памагитееее, значит не все еще потеряно, есть шанс понять тему😂
I'm Siberian Уровень 109 Expert
19 сентября 2024
либо статус "памагити" не изменился )
Олег Уровень 106 Expert
18 июля 2024
я полностью потерялся.
Денис Уровень 67
5 марта 2025
Уже нашлись на уровне 106
Олег Уровень 79 Expert
13 июля 2023
Было бы недурно буковок объясняющих добавить.... Тема сложная, а они нас за сеньёров держат)))
jvatechs Уровень 111 Expert
26 марта 2023
Задания! Такая сложная тема, а заданий код наплакал.
Misha Saharin Уровень 111 Expert
8 ноября 2022
Это когда ты про стримы думал - это потолок твоего осознания этой науки. Теперь, треды взвинтили этот пик и ощущаешь себя на марсе. Если дальше ждёт цифровой симбиоз потоков вложенных в треды - я уйду в "вырезаноЦензурой" бесконечность соседних вселенных. )
Мая Уровень 82
29 июля 2025
вам бы фантастику писать а
Мурат Шорманов Уровень 51
17 июля 2022
Последний код вывел в идее в итоге false вывело
Volodymyr Horbachev Уровень 63 Expert
18 июня 2023
У меня так же. Не понял когда выдает TRUE?
Константин Уровень 100 Expert
13 июля 2022
В чем отличие метода boolean isShutdown() от boolean isTerminated()?
Екатерина Уровень 70 Expert
17 сентября 2022
Может еще кому будет полезно: Насколько я понимаю: ExecutorService - большой кабель, который вмещает в себя маленькие кабели (обычные thread). Картинка для ассоциации - он работает на прием новых поток и их запуск или остановку - а обычные thread как работали так и работают, но внутри него. isShutdown() - спрашивает, Большой кабель, принимаешь новые потоки? isTerminated() - спрашивает, Большой кабель, ты все потоки маленькие завершил что у тебя были? Коллеги, буду благодарна за уточнения и корректировки)) тема дается с болью)
Константин Уровень 100 Expert
18 сентября 2022
Спасибо, Катя, за пример!)
Misha Saharin Уровень 111 Expert
5 ноября 2022
пример - супер СпасибоОгромное
Руслан Уровень 46
27 октября 2025
isShutdown() просто проверяет был ли вызван shatdown или shatdownNow а isTerminated() проверяет завершились ли задачи после вызова shatdown или shatdownNow
Андрей Пазюк Уровень 122 Expert
2 июня 2022
До этого я мало что понимал, а теперь вообще ничего не понимаю.
Сергей Уровень 4
2 июня 2022
Согласен, задач, помогающих освоить материал, нет. Дали бы подводящие задачи уровня Easy, следом медиум и хард, чтоб прокачаться. а то, как- то очень обзорно всё...