Почему опасно выполнять блокирующие операции в стадиях CompletableFuture без явного Executor?
Async-стадии без Executor выполняются в ForkJoinPool.commonPool — он общий на всю JVM и размером примерно число ядер − 1. Блокирующие вызовы (HTTP, БД, get чужого future) занимают его потоки, и встаёт всё, что им пользуется: другие цепочки, parallel stream, библиотеки.
- Для блокирующих шагов — свой пул: supplyAsync(task, ioExecutor)
- Симптом: приложение «тормозит» в коде, который не менялся
- Виртуальные потоки — хороший executor для блокирующих стадий
А ещё В контейнере с 1–2 CPU commonPool совсем крошечный
Подробно
Почему commonPool не подходит для ввода-вывода. Он задуман для коротких вычислительных задач без блокировок; его параллелизм равен availableProcessors() - 1. На машине с 4 ядрами — 3 потока. Три сетевых вызова по секунде — и все async-стадии приложения ждут.
Особенность контейнеров. JVM видит лимит CPU контейнера: при 1 CPU commonPool может состоять из одного потока, а async-задачи вообще выполняться в новом потоке на каждую.
Правильно:
ExecutorService io = Executors.newVirtualThreadPerTaskExecutor();
supplyAsync(() -> http.get(url), io)
.thenApplyAsync(this::parse, cpuPool);
Блокирующие стадии — в отдельный пул (ограниченный, именованный, или виртуальные потоки), вычислительные можно оставить в commonPool.
То же касается parallelStream() с сетевыми вызовами внутри.
Что хотят услышать
- Какой пул используется по умолчанию и его размер.
- Почему блокировки в нём вредят всему приложению.
- Решение: свой executor.
Частые ошибки
- Делать блокирующий I/O в commonPool.
- Думать, что commonPool безграничен.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✓Стадии идут в общий commonPool размером по числу ядер, и блокировки его истощают
- ✗commonPool однопоточный, и любая блокирующая стадия останавливает его
- ✗CompletableFuture без Executor вообще не использует потоки
- ✗Блокировки в commonPool бросают IllegalStateException
Что спросят дальше
- Чем отличается thenApply от thenApplyAsync?
- Как обрабатывать исключения в цепочке (exceptionally/handle)?
Разбор темы
- Future и CompletableFuture
Ограничения Future, асинхронная композиция CompletableFuture, async-варианты с пулами и обработка ошибок.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.