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



Мы привыкли, что многопоточность - это классическая борьба с блокировками потоков и бесконечные синхронайзы, но эпоха реактивного программирования (Project Reactor, RxJava) на внутреннем бэкенде подходит к концу. С выходом Project Loom в JDK 21 и развитием Structured Concurrency в JDK 23–24, Java-разработчикам больше не нужно выбирать между простотой блокирующего кода и производительностью асинхронного.

Если в проекте до сих пор используется newFixedThreadPool для сетевых вызовов — мы платим производительностью за привычки 2010-х годов. Вот что нужно знать о современной конкурентности в Java.

1. Virtual Threads: «Один запрос — один поток» возвращается
Виртуальные потоки (Virtual Threads) — это легковесные потоки, которые не привязаны к потокам операционной системы.
Зачем это нужно: Классический newFixedThreadPool(200) упирается в лимит ОС при 10 000 одновременных запросов. Виртуальные потоки позволяют запустить миллион потоков в одном JVM без деградации.
Где применять: I/O-bound задачи. Вызовы к базам данных (JDBC), HTTP-клиенты, RPC. Код остается простым и синхронным (try-with-resources, обычные циклы), но под капотом JVM ставит поток на паузу, пока он ждет сеть, освобождая ресурсы.
Важное правило: Не пулируйте виртуальные потоки. Используйте Executors.newVirtualThreadPerTaskExecutor(). Создавать их дешево, переиспользование больше не требуется.

2. Structured Concurrency
Обычный ExecutorService легко порождает «сиротские» задачи: родительский запрос отменился из-за таймаута, а фоновый HTTP-вызов к CRM продолжает висеть в thread dump. Structured Concurrency решает эту проблему, заставляя группу потоков вести себя как единый блок.
Как это работает: Вы создаете область (scope), запускаете в ней задачи и ждете их все разом. Если одна задача падает или родительский поток отменяется, остальные задачи в этой области автоматически прерываются. Никаких утечек, никаких «зомби-потоков».
Пример: Обработка заказа, где нужно параллельно сходить в инвентарь и службу лояльности. Если инвентарь ответил ошибкой, нет смысла ждать ответ от лояльности — транзакция всё равно откатится.

3. Scoped Values
ThreadLocal — главный источник утечек памяти и проблем при пуллинге потоков. В мире виртуальных потоков, которые создаются и уничтожаются миллионами, ThreadLocal становится токсичным.
Решение: Scoped Values (стабильны с JDK 25). Это неизменяемая передача контекста. Значение доступно только в рамках текущего метода и его дочерних вызовов. Оно не требует ручной очистки в finally-блоке и безопасно для виртуальных потоков.

4. Подводные камни
Переход на Loom не является бесплатным апгрейдом:
Pinning (Прибивание): Если виртуальный поток заходит в блок synchronized или вызывает нативный код, он «прибивается» к потоку ОС и не может его освободить. В JDK 24 проблема synchronized практически решена, но старые драйверы БД всё еще могут стать узким местом.
Бенчмарки CPU: Виртуальные потоки не делают ваш код быстрее, если он нагружает процессор (вычисления, хэширование). Для CPU-bound задач по-прежнему используйте ForkJoinPool или фиксированные пулы.
Инструментарий: Профилировщики и дебаггеры (особенно в версиях 21–23) могут работать медленнее или показывать странные стек-трейсы из-за механизма continuations.

Итог: Современный энтерпрайз-Java в 2026 году — это синхронный, легко читаемый код, который масштабируется лучше, чем сложные реактивные пайплайны. Умение грамотно применять newVirtualThreadPerTaskExecutor и StructuredTaskScope — это маркер Senior-разработчика, готового к высоким нагрузкам.
Просмотры: 8
Открыть в MAX Сообщить о неточности

Репосты публикации

Публикация канала

Сообщить о неточности

Опишите, что нужно исправить. Если есть источник, добавьте ссылку.

Вы покидаете datool.ru

Ссылка ведёт на внешний сайт. datool.ru не отвечает за его содержимое — проверьте адрес, прежде чем вводить пароли или платёжные данные.

Переход через 10 с

Перейти