Виртуальные потоки: как устроены и когда брать
Вопрос
Расскажите, как работают виртуальные потоки и стоит ли переводить на них сервис на Spring Boot.
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
Платформенный поток = поток ОС, дорогой; виртуальный управляется JVM
Continuation: при блокировке стек сохраняется в куче, носитель освобождается
Выигрыш в пропускной способности для I/O, не для CPU
Pinning в synchronized в Java 21-23, исправлен в 24
Не пулить, ограничивать семафорами; осторожно с ThreadLocal
Spring Boot 3.2+: spring.threads.virtual.enabled, проверить пул соединений БД
Что могут спросить следом
- Чем это отличается от реактивного подхода WebFlux?
- Что станет узким местом после перехода?
Разбор
Обычный поток Java это поток ОС: мегабайт под стек, переключение через ядро, тысячи на сервер. Виртуальный поток это объект JVM: его стек хранится в куче как continuation, исполняется он на небольшом пуле потоков-носителей, по умолчанию ForkJoinPool размером с число ядер. Когда виртуальный поток блокируется на сокете, JVM сохраняет его состояние и отдаёт носитель другому. Поэтому можно держать миллионы потоков и писать простой блокирующий код вместо реактивного. Выигрыш только для I/O-нагрузки. Нюансы: в Java 21 блокировка внутри synchronized приколачивает поток к носителю, это исправили в 24; потоки не пулят, ограничение делают семафором; ThreadLocal с большими объектами умножается на число потоков. В Spring Boot 3.2+ включается одним свойством. Перед включением смотрю, что станет узким местом: обычно это пул соединений к БД, и тысячи потоков просто встанут в очередь за соединением.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.
Термины по теме
Ещё по теме «java.util.concurrent и виртуальные потоки»
- Что выведет: цепочка CompletableFuture
- В каком потоке выполнится thenApply
- Гонка на ConcurrentHashMap
- Рекурсия в computeIfAbsent
- null в ConcurrentHashMap
- CompletableFuture без executor
- Опасность newFixedThreadPool
- CountDownLatch и CyclicBarrier