Виртуальные и платформенные потоки в Java
Снаружи виртуальный и платформенный поток выглядят
одинаково: тот же Thread, те же start,
join и прерывание. Разница в том, кто
выделяет стек и кто переключает выполнение: ядро ОС
или JVM на своих носителях.
Платформенный поток один к одному опирается на поток операционной системы. Его мало, каждый дорог по памяти, зато он предсказуем для долгой CPU‑работы и для кода, который уже привязан к платформенной модели.
Виртуальный поток дешёв в создании, JVM
может держать очень много экземпляров. Пока код
блокируется на стандартном I/O или sleep,
носитель освобождается. Если же внутри
synchronized или нативного участка долго
крутится вычисление, носитель может закрепиться
и снизить выгоду модели, поэтому тяжёлые критические
секции для виртуальных задач лучше пересмотреть.
Два потока в одной программе и проверка типа:
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread platform = Thread.ofPlatform()
.name("os-worker")
.start(() -> printKind());
Thread virtual = Thread.startVirtualThread(Main::printKind);
platform.join();
virtual.join();
}
static void printKind() {
Thread t = Thread.currentThread();
System.out.println(t.getName() + " virtual=" + t.isVirtual());
}
}
Для os-worker будет virtual=false,
для виртуального потока true. Главный поток
тоже платформенный, это нормально для точки входа.
Практическое правило для Java 27: массовые
блокирующие запросы и фоновые ожидания чаще ведут
на виртуальные потоки или newVirtualThreadPerTaskExecutor;
ограниченный параллелизм вычислений и работа с
legacy‑библиотеками, чувствительными к типу потока,
остаются на платформенных пулах с явным размером.