Что такое thread pool и для чего он используется в Java?
Что проверяют
Понимаете ли вы, зачем ограничивать число потоков, и знаете ли опасность стандартных фабрик.
Как отвечать
Пул потоков это набор заранее созданных потоков, разбирающих задачи из очереди. Смысл в двух вещах.
Первая: переиспользование. Создание потока это системный вызов, выделение стека в мегабайт и работа планировщика ядра. На коротких задачах создание обходится дороже самой работы.
Вторая, более важная: ограничение. Без пула тысяча одновременных запросов создаст тысячу потоков, и приложение умрёт от переключений контекста и расхода памяти. Пул превращает всплеск нагрузки в очередь, а очередь это то, чем можно управлять.
Как устроен: количество потоков в базовом состоянии и максимальное, время простоя дополнительных, очередь задач, фабрика потоков и политика отклонения при переполнении.
Порядок принятия задачи неочевиден и его стоит знать: пока потоков меньше базового числа, создаётся новый; дальше задача идёт в очередь; и только когда очередь заполнена, создаются потоки сверх базового числа. Значит неограниченная очередь означает, что дополнительные потоки не появятся никогда.
Отсюда главное предупреждение: стандартные фабричные методы опасны в проде. Пул фиксированного размера использует неограниченную очередь, и она растёт до исчерпания памяти. Кэширующий пул создаёт неограниченное число потоков. В рабочем коде пул создают явно, с ограниченной очередью и осмысленной политикой отклонения. Полезная политика это выполнение задачи в вызывающем потоке: она естественным образом притормаживает поставщика.
Мелочи, которые отличают опытного: обязательно именовать потоки через фабрику, иначе дамп бесполезен; исключение в задаче, отправленной с получением результата, молча оседает в объекте результата; пул надо закрывать, иначе приложение не завершится.
С двадцать первой версии для задач с ожиданием появились виртуальные потоки, где ограничивать число потоков больше не нужно.
Где спотыкаются
Берут фиксированный пул и получают неограниченный рост очереди.
Не проверяют результат и теряют исключения.
Выполняют блокирующие вызовы в пуле, рассчитанном на вычисления.
Не именуют потоки и не могут разобрать дамп.