Обновить

Комментарии 7

Крутая статья, спасибо!

Возник вопрос: если провести параллель с C++ - где у нас есть системные threads и примитивы синхронизации вроде mutex/condition_variable - то как виртуальные потоки в Java взаимодействуют с настоящими системными потоками под капотом?

Есть ли там какая-то «скрытая» синхронизация на уровне carrier-threads? Например, пулы, мьютексы, шедулинг очередей задач? Или виртуальный поток полностью избегает тяжёлых блокировок и переключается без участия ОС?

Есть carrier-threads(настоящий поток ОС), в котором может быть много виртуальных потоков.
Виртуальные потоки не держат стек в памяти ОС, их стек хранится в куче и выделяется динамически.

На уровне JVM есть планировщик виртуальных потоков. Он управляет тем, какой виртуальный поток монтировать на какой carrier-thread.
Планировщик использует FIFO-очередь (First-In-First-Out) для ожидания выполнения задач на carrier-thread.
Также же еще весь сок в том, что виртуальные потоки полностью совместимы со старым апи, то есть как раз и экзекуторы, симафоры и т.д)

Спасибо за комментарий, рад, что вам понравилась статья)

JVM старается свести к минимуму использование системных примитивов синхронизации. Они во-первых медленные, во-вторых плохо переносимые между платформами. Причем это верно и для виртуальных потоков, и для старых, «настоящих».

Есть низкоуровневый класс LockSupport, который предоставляет что-то вроде простенького семафора (park, unpark, parkNanos, …) и делегирует это ядру ОС. Он используется только после когда испробованы неблокирующие методы (барьеры, compare-and-swap), но все равно требуется настоящая блокировка. Все высокоуровневые ReentrantLock/BlockingQueue/итп завязаны на LockSupport и содержат кучу неблокирующего кода.

Парковать виртуальные потоки на уровне ОС нужно еще реже. Часто два виртуальных потока висят на одном настоящем, это легко проверяется. Если парковка все-таки нужна, виртуальный поток будет предварительно размонтирован (то есть освободит системный поток для других).

В JVM 24/25 наконец пофиксили баг с пиннингом synchronized, и вообще красота настала. Старый Jetty-сервер стал держать тысячи соединений, практически без изменения кода (только последний Jetty нужен)

Совершенно зря назвали их тоже потоками. Получается путаница, ведь в реальности виртуальный поток отличается от обычного почти так же, как поток от процесса (количестенно, не качественно).

Тем более, что когда есть поток поверх потока, то возникает ощущение, что можно и дальше наслаивать — поток поверх потока поверх потока, а это совсем не так.

Если я не ошибаюсь основная причина, почему их назвали виртуальными потоками: обеспечить полную совместимость с существующим API потоков в Java)

Спасибо. Интересная статья.

Рад, что вам понравилось)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации