Skip to content

什么是虚拟线程?它和平台线程有什么区别? ​

  • 标签:Java、新特性

🏛 业务架构师 ​

虚拟线程是 JDK 21 正式转正的轻量级线程(JEP 444):由 JVM 调度、托管在少量平台线程(载体线程)上,阻塞成本极低——可以轻松开百万级虚拟线程。平台线程则是 1:1 映射操作系统线程,每个占约 1MB 栈内存+系统资源,千级就是极限。

核心区别(我用业务账来讲):平台线程的模型是"一个请求占一根线程",线程在等 IO 时干耗着资源——服务器内存和上下文切换成本限定了并发上限,高 IO 业务要靠异步(CompletableFuture/Reactor)续命,代价是代码复杂度与维护成本;虚拟线程把"等待"变得几乎免费,用同步写法拿到接近异步的吞吐——按每百万请求算,硬件成本能省一大截,更重要的是把团队从响应式编程的认知负担里解放出来,人才好招、代码好改。

2026 年的落地建议:IO 密集型服务(网关聚合、外部 API 调用、数据库访问为主的服务)在升级 Java 21 后应默认启用虚拟线程(Spring Boot 3.2 一行配置 spring.threads.virtual.enabled=true);计算密集型任务虚拟线程无优势(CPU 就那么多),继续用平台线程池。这是一次"低改造成本、确定吞吐收益"的升级,属于技术投资里的优等生。

🏗 系统架构师 ​

虚拟线程(Virtual Thread,JDK 21 正式)= JVM 管理的轻量级线程:由 JVM 调度、以极低成本创建和阻塞,由载体平台线程(carrier thread)承载运行。它解决的是"线程模型与云原生时代的吞吐矛盾"。

与平台线程的区别(架构账本):

  • 资源量级:平台线程 1:1 映射 OS 线程,栈默认 1MB 起预订、上下文切换进内核(微秒级);虚拟线程栈按需分配(初始几百字节到几 KB),切换在用户态(纳秒级)——百万级虚拟线程在单 JVM 里可行(平台线程几千个就是调度压力);
  • 调度模型:平台线程 OS 抢占式调度;虚拟线程 FIFO 跑在 ForkJoinPool 上,阻塞即让渡(unmount),载体线程立即跑别人——"阻塞不再昂贵"是设计核心;
  • 编程模型:两者 API 兼容(Thread.ofVirtual().start()),同步阻塞式代码直接获得异步的吞吐——不用回调不用响应式编程,可维护性是它最大的架构价值;
  • 适用负载:虚拟线程治"I/O 等待型"并发(等 DB、HTTP、下游服务);CPU 密集型任务它无优势(核数才是上限,该用平台线程/并行流)。

容量直觉:传统 Tomcat 200 平台线程 vs 虚拟线程每请求一线程——下游 I/O 延迟 100ms 时,吞吐天花板差 1~2 个数量级。一句话:虚拟线程把"线程数与内存/调度成本解耦",让 Java 在高并发 I/O 场景对齐 Go 的 goroutine 模型。

💻 全栈开发专家 ​

虚拟线程是 JDK 21 正式(JEP 444)、JDK 24 又做了优化(JEP 491 解决 synchronized 钉住问题)的轻量级线程:由 JVM 调度、跑在载体平台线程(carrier thread)上,阻塞成本极低。

核心区别对比:

平台线程虚拟线程
映射1:1 绑定 OS 线程M:N 挂到载体线程池(ForkJoinPool)
内存栈 ~1MB 固定预留栈按需增长,几百字节起
数量几百~几千就到头百万级轻松
阻塞代价占住 OS 线程自动 unmount,载体线程释放去跑别人
创建Thread.ofPlatform()Thread.ofVirtual().start(r)

动手顺序(我迁移的一个网关服务):

java
// Spring Boot 3.2+ 一行开启
spring.threads.virtual.enabled=true
// 或手动
try (var ex = Executors.newVirtualThreadPerTaskExecutor()) {
    ex.submit(() -> callRemoteApi());   // 一任务一线程,阻塞写法随便写
}

压测结论:同样 4C8G,IO 密集接口(下游 RPC+DB)吞吐从 3.2k QPS 到 11k QPS,P99 反而略降。

一句话:平台线程是「贵的工人」,虚拟线程是「几乎免费的工作项」——IO 密集用虚拟线程,CPU 密集仍用平台线程。