JavaRush /Курсы /Java Multithreading /ThreadPoolExecutor пул нитей

ThreadPoolExecutor пул нитей

Java Multithreading
8 уровень , 7 лекция
Открыта
ThreadPoolExecutor пул нитей - 1

— Рядовой программист рано или поздно сталкивается с тем, что у него есть много маленьких задач, которые нужно выполнять время от времени.

Если ты пишешь игру, то это действия, которые выполняют отдельные персонажи.

Если пишешь веб-сервер, то это различные команды, приходящие от пользователей: загрузить фото, перекодировать его в нужный формат, применить нужный шаблон и т.д.

Все большие задачи рано или поздно разбиваются на набор маленьких и удобных задач.

Вот так на этом фоне незаметно и возникает вопрос – а как ими всеми управлять? Если в минуту нужно выполнить несколько сотен задач? Создавать для каждой задачи свою нить бывает не очень рационально. Для каждой нити Java-машина выделяет довольно много ресурсов.

Другими словами – создание и уничтожение отработавшей нити может тратить больше ресурсов и времени, чем само выполняемое задание.

Java-разработчики придумали элегантное решение этой проблемы — ThreadPoolExecutor.

ThreadPoolExecutor – это класс, который имеет внутри две вещи:

А) Очередь задач, в которую можно добавлять задачи, по мере их появления в программе.

Б) Пул-нитей (группа нитей) – которые эти задачи исполняют.

При этом нити не уничтожаются после выполнения задания, а засыпают. Чтобы начать выполнять новое задание, как только оно появится.

При создании ThreadPoolExecutor, можно задать максимальное количество нитей, которые будут созданы и максимальное количество заданий, которые можно поместить в очередь. Т.е. можно ограничить количество нитей числом 10, например, а количество задач в очереди – 100.

Как работает ThreadPoolExecutor:

1) При добавлении нового задания, оно помещается в конец очереди.

2) Если очередь заполнена будет выкинуто исключение.

3) Каждая нить после выполнения задания берет очередное задание из очереди и начинает выполнять его.

4) Если задач в очереди нет, нить засыпает до их добавления.

Подход с ограничением количества работающих нитей выгоден тем, что чем больше нитей, тем сильнее они друг другу мешают. Гораздо эффективнее иметь 5-10 нитей-исполнителей и длинную очередь задач, чем создать 100 нитей для внезапно появившейся группы задач, которые будет конкурировать друг с другом за ресурсы: память, время процессора, доступ к базе и т.п.

Пример работы такого ThreadPoolExecutor:
<

Пример
ExecutorService service = Executors.newFixedThreadPool(2);

for(int i = 0; i < 10; i++) {
 service.submit(new Runnable() { public void run() 
    {
     // тут мы загружаем что-то тяжелое из интернета.
    }
 });
}

— Я что-то не вижу его…

— Объект ThreadPoolExecutor создается при вызове метода newFixedThreadPool.

Так вот, работает он очень просто. Как только ты добавляешь ему задачу с помощью метода submit, он:

А) Будит спящую нить для ее выполнения, если такая есть.

Б) Создает новую нить для выполнения задания, если ее нет.

В) Если достигнут максимум нитей, то просто кладет задачу в конец очереди.

Я специально написала в примере – тут мы загружаем что-то тяжелое из интернета. Если у нас есть 100 задач «скачать что-то большое из интернета», то нет смысла запускать много таких задач одновременно – мы упремся в ограничение ширины интернет-канала. В таком случает пары нитей должно быть достаточно. Именно это ты и видишь в примере выше:

ExecutorService service = Executors.newFixedThreadPool(2);

— Оказывается, работать с кучей задач не так уж и сложно.

— Да. Даже легче, чем ты можешь себе представить. Но об этом тебе расскажет Ким.

Комментарии (47)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Алексей Уровень 1
1 мая 2025
С детства обожаю эти карикатуры. Со мной все нормально?
Камушек Уровень 36
23 декабря 2023
Thread'ом Java не испортишь: Часть V — Executor, ThreadPool, Fork Join ExecutorService Java ThreadPoolExecutor prestartAllCoreThreads() shutdown() - ждет завершения запущенных задач, ничего не возвращает. shutdownNow( )- останавливает исполнитель немедленно и возвращает список незавершенных задач.
alexyng7 Уровень 32
14 ноября 2023
Rolik Уровень 41
26 апреля 2023
Честно ? Ору... Просто представил поплавленный мозг человека, на которого так сходу вывалили 2 интерфейса и 3 класса создания и управления пулом потоков и чтоб наверняка, добили лопатой лямбдой. И все это в одной коротенькой лекции. :)
Taurnil Уровень 51
3 июня 2023
Это не лямбда, это анонимный класс. Эта штука уже давно должна была в голове осесть и восприниматься как должное, столько уже задач было, где анонимные классы явно или неявно приходилось использовать. Лямбда выглядела бы вот так:

service.submit(() -> doExpensiveOperation(i));
А по теме очередей потоков рекомендую посмотреть Алишева. У него несколько роликов есть про многопоточность, где подробно разбирается эта(ThreadPoolExecutor, Executors, ExecutorService) тема.
Rolik Уровень 41
4 июня 2023
Какой раздел и номер уровня ? Хочу посмотреть где ошибся.
Taurnil Уровень 51
4 июня 2023
Анонимные класы явно разбираются в этом разделе: https://javarush.com/quests/lectures/questmultithreading.level03.lecture07 Но первое упоминание было в конце Syntax, в разделе с лямбдами или около того. https://javarush.com/quests/lectures?quest=QUEST_JAVA_SYNTAX&level=19 Лямбда, по отношению к переопределению метода функционального интерфейса, это, по сути, упрощенно-обобщенная запись объявления анонимного класса. Через такую парадигму гораздо проще это понять. Если ты не разбираешься в задачах или копируешь готовое решение, не вникая в тему (потому что эта тема еще явно не разжевывалась), то хотя бы не решай их, а возвращайся к ним позднее, когда понимание придет.
Rolik Уровень 41
4 июня 2023
Я просил ссылку на тему где я оставил комментарий. Я хочу проверить - действительно ли я ошибся, и анонимный класс в примере/задаче был объявлен без использования лямбда выражения. По сути, лямбда выражение и есть сокращенная запись анонимного внутреннего класса.
Taurnil Уровень 51
4 июня 2023
Что ты имеешь ввиду? Тебе нужна ссылка на текущую ветку штолле? XD https://javarush.com/quests/lectures/questmultithreading.level08.lecture07
Rolik Уровень 41
4 июня 2023
Именно. Я из аппы не могу выйти обратно в тему. Да, лямбды в теме нет. Очевидно перелистывая конспект, спутал примеры в JR и конспекте. Приношу извинения.
Kirill Уровень 46
3 декабря 2024
Между лямбдами и анонимными классами есть существенная разница: лямбды, в отличие от анонимных классов, не имеют собственного this (их контекст исполнения - окружающий их объект/класс). Соответственно после компиляции для лямбда выражений не создаётся отдельный class-файл.
BALENCIAGA DEV Уровень 51
11 апреля 2025
Я надеюсь у вас все хорошо и вы во всем разобрались, извините но я просто в ахуе с того какие softskills проявляет человек у которого прописано Technical Lead в ООО "Теком". Я теперь начинаю понимать откуда возник такой вой вокруг данной темы.
Griboed Уровень 30
5 апреля 2023
чувствую я, тема маст хэв. =)
22 ноября 2022
Ничего не сказано про shutdown. Возможно это лишняя, мусорная информация. Но это не точно.
Zhenya Workout Уровень 43
20 марта 2023
Да, остановить сервис это мусорная задача)
Gans Electro Уровень 4
23 июня 2023
Дальше есть парни
Bakyt Sarmanov Уровень 50
1 июня 2022
Пример работы такого ThreadPoolExecutor: < как же хочется убрать это😀
11 апреля 2021
 
newFixedThreadPool(2)
 
2 - обозначает, что создается пул с двумя потоками.
Роман Юрьевич Уровень 38
14 августа 2021
Информация для тех, кто дошел до данного уровня просто копируя решения задач из комментариев😄
Уровень 51
25 декабря 2022
М?А у меня есть волшебная кнопка "Показать решение"...
Борис Скворцов Уровень 41
5 декабря 2020
Зачем ограничивать очередь задач? На что это влияет?
Flexo Уровень 41
12 июля 2021
по умолчанию ограничение очереди в Integer.MAX_VALUE, это с лихвой хватает для всего но можно и ограничить, что, видимо, позволит управлять ресурсами занимаемой памяти
CyberBoar Уровень 1
3 августа 2022
Видимо для того, чтобы не создавать ситуаций, когда очередь заполнена невыполнимым количеством задач.
Виктор Уровень 1
2 октября 2022
ограничивается не очередь,а количество одновременно работающих нитей.