Java 22 虚拟线程
·
Java 22 的虚拟线程(Virtual Threads)是 Project Loom 正式落地的轻量级线程,由 JVM 调度、不绑定 1:1 操作系统线程,主打极低内存开销、百万级并发、同步编程模型。下面从概念、对比、用法、示例、适用场景与避坑一次性讲清楚。
一、什么是虚拟线程
- 平台线程(传统 Thread):1:1 映射 OS 线程,栈默认 1MB,创建 / 切换开销高,数量受限(通常几千)。
- 虚拟线程(Virtual Thread):
- 由 JVM 用户态调度,挂载在少量平台线程(载体线程)上。
- 栈内存KB 级,创建开销接近零,可轻松创建百万级。
- 阻塞(如 sleep、I/O、JDBC)时自动让出载体线程,不阻塞 OS 线程,吞吐量暴涨。
简单说:虚拟线程 = 轻量、海量、不占 OS 线程的 “用户态线程”。
二、虚拟线程 vs 平台线程(Java 22)
表格
| 特性 | 虚拟线程 | 平台线程 |
|---|---|---|
| 调度方 | JVM(用户态) | OS(内核态) |
| 栈内存 | ~200KB(可更小) | ~1MB |
| 创建开销 | 极低(对象分配级) | 高(OS 资源) |
| 最大数量 | 百万级(无硬限) | 数千(OS 限制) |
| 阻塞影响 | 不阻塞载体线程 | 阻塞 OS 线程 |
| 适用场景 | I/O 密集(DB、HTTP、RPC) | CPU 密集、临界区 |
三、Java 22 核心用法(直接可运行)
1)直接创建虚拟线程
java
运行
// 方式1:创建并启动
Thread vt1 = Thread.ofVirtual()
.name("vt-", 0) // 命名前缀+自增
.start(() -> {
System.out.println("虚拟线程运行:" + Thread.currentThread());
try { Thread.sleep(1000); } catch (InterruptedException e) {}
});
// 方式2:先创建、后启动
Thread vt2 = Thread.ofVirtual().unstarted(() -> { ... });
vt2.start();
vt2.join(); // 等待完成
2)虚拟线程执行器(推荐,替代线程池)
Executors.newVirtualThreadPerTaskExecutor():每个任务一个虚拟线程,无需池化。
java
运行
import java.util.concurrent.Executors;
public class VtDemo {
public static void main(String[] args) {
// 自动关闭(try-with-resources)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 提交 10 万任务,轻松运行
for (int i = 0; i < 100_000; i++) {
int id = i;
executor.submit(() -> {
// 模拟 I/O 阻塞(DB/HTTP)
try { Thread.sleep(200); } catch (InterruptedException e) {}
System.out.println("任务 " + id + " 完成");
});
}
}
// executor 关闭后等待所有虚拟线程结束
}
}
3)结构化并发(Java 22 增强,推荐)
用 StructuredTaskScope 管理虚拟线程生命周期,父线程等待所有子线程完成,失败自动取消。
java
运行
import java.util.concurrent.StructuredTaskScope;
public class StructuredVt {
public static void main(String[] args) throws Exception {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 提交多个虚拟线程任务
var task1 = scope.fork(() -> {
Thread.sleep(500);
return "结果1";
});
var task2 = scope.fork(() -> {
Thread.sleep(800);
return "结果2";
});
scope.join(); // 等待所有任务完成
scope.throwIfFailed(); // 任一失败则抛异常
System.out.println(task1.get() + ", " + task2.get());
}
}
}
四、Java 22 相比 21 的关键改进
- 调度器重写:工作窃取算法,并发能力提升约 30%。
- ThreadLocal 优化:虚拟线程中使用 ThreadLocal 性能显著提升。
- 监控增强:JFR 支持虚拟线程事件(创建、挂起、恢复)。
- 结构化并发稳定:
StructuredTaskScope正式转正,更安全易用。
五、适用场景(一句话判断)
- ✅ I/O 密集型(首选):Web 请求、DB 查询、HTTP 调用、RPC、消息消费。
- ✅ 高并发微服务:Spring Boot 3.2+、Tomcat 10+ 可直接启用虚拟线程处理请求。
- ❌ CPU 密集型:虚拟线程不加速计算,用平台线程池 + 并行流更合适。
- ❌ 频繁同步锁 / 竞争:虚拟线程阻塞在锁上仍会占用载体线程,需优化锁粒度。
六、避坑指南(生产必看)
- 不要池化虚拟线程:
newVirtualThreadPerTaskExecutor是 “每个任务一线程”,池化无意义且复杂。 - 避免在虚拟线程中阻塞平台线程池:
java
运行
// 反例:虚拟线程内调用固定平台线程池,会被池大小限制 ExecutorService platformPool = Executors.newFixedThreadPool(10); try (var vtExec = Executors.newVirtualThreadPerTaskExecutor()) { vtExec.submit(() -> platformPool.submit(() -> {})); // 瓶颈 } - 慎用 ThreadLocal:虽优化,但百万虚拟线程下仍可能内存泄漏,优先用作用域变量(ScopedValue,Java 22 新)。
- 监控虚拟线程:
bash
运行
# 查看虚拟线程数、载体线程数 jcmd <pid> Thread.print -l
七、快速迁移(老项目改造三步)
- 升级 JDK 到 22,框架到 Spring Boot 3.2+/Tomcat 10+。
- 替换线程池:
java
运行
// 旧:固定线程池 ExecutorService oldPool = Executors.newFixedThreadPool(200); // 新:虚拟线程执行器 ExecutorService vtPool = Executors.newVirtualThreadPerTaskExecutor(); - 启用 Web 虚拟线程(Spring Boot):
properties
# application.properties spring.threads.virtual.enabled=true
总结
Java 22 虚拟线程是高并发 I/O 场景的最优解:用同步代码写异步逻辑,百万级并发不宕机,内存开销极低。核心是 **Thread.ofVirtual() + newVirtualThreadPerTaskExecutor() + 结构化并发 **。
更多推荐


所有评论(0)