Как работает fixedThreadPool при 10 потоках и 11 задачах
Что проверяют
Знаете ли вы, что внутри Executors.newFixedThreadPool и как ThreadPoolExecutor решает, создать поток, поставить задачу в очередь или отклонить. Следом обычно спрашивают, чем опасна очередь этого пула под нагрузкой.
Как отвечать
Сначала раскройте фабрику: newFixedThreadPool(10) это new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()). Ядро и максимум равны десяти, очередь без явной границы, её ёмкость Integer.MAX_VALUE.
Дальше пройдите по алгоритму execute:
- задачи с первой по десятую: пока потоков меньше
corePoolSize, на каждую задачу создаётся новый поток, даже если созданные раньше простаивают. При старте пула потоков нет; - одиннадцатая задача: ядро заполнено, задача кладётся в
LinkedBlockingQueue. Одиннадцатый поток не создаётся; - дальше: первый освободившийся поток сам забирает задачу из очереди. Какой именно, заранее не известно;
- после выполнения: потоки не завершаются, а ждут новых задач, таймаут для ядра по умолчанию выключен.
Одновременно десять задач выполняются только при десяти свободных ядрах, иначе ОС делит между ними процессорное время.
Завершение: shutdown() перестаёт принимать новые задачи и даёт доделать принятые, включая одиннадцатую, а shutdownNow() прерывает потоки и возвращает невыполненное. С Java 19 ExecutorService реализует AutoCloseable. Для задач, которые в основном ждут ввод-вывод, в Java 21 есть Executors.newVirtualThreadPerTaskExecutor().
Где спотыкаются
Говорят, что под одиннадцатую задачу пул создаст ещё поток или отклонит её. Потоки сверх ядра создаются только при заполненной очереди, а эта очередь практически бесконечна, так что RejectedExecutionHandler до вызова shutdown() не сработает.
Не видят риска неограниченной очереди. Если задачи приходят быстрее, чем выполняются, очередь растёт до OutOfMemoryError. В проде пул создают через конструктор с ArrayBlockingQueue и явной политикой отказа.
Забывают про shutdown(). Потоки пула не демоны, и JVM не завершится, пока пул жив. Ещё ловушка: исключение из задачи, отправленной через submit, прячется в Future и никуда не печатается.