🧵 Java 虚拟线程(Virtual Threads)简介
虚拟线程是 JDK 19 中引入的预览特性,在 JDK 21 中正式成为标准 API。它是一种轻量级线程,由 JVM 管理,极大地提升了并发处理能力。
1️⃣ 什么是虚拟线程?
虚拟线程是 Java 平台提供的轻量级线程实现,也被称为“用户态线程”或“协程”。
- 它们不是由操作系统调度,而是由 JVM 内部调度。
- 可以轻松创建数百万个虚拟线程,而不会对系统资源造成压力。
Thread virtualThread = Thread.ofVirtual().name("my-vt").start(() -> {
System.out.println("Hello from virtual thread");
});
2️⃣ 虚拟线程 vs 操作系统线程
特性 | 操作系统线程(Platform Threads) | 虚拟线程(Virtual Threads) |
---|---|---|
创建成本 | 高:每个线程占用 MB 级内存 | 极低:每个线程仅几 KB 内存 |
调度 | 操作系统内核调度 | 用户态由 JVM 调度 |
上下文切换 | 昂贵 | 轻量快速 |
数量上限 | 几千个线程就会导致 OOM 或性能下降 | 数百万线程可轻松运行 |
使用场景 | CPU 密集型任务 | I/O 密集型任务、高并发服务 |
3️⃣ 核心优势
- 极高的并发能力:支持同时运行数十万甚至上百万并发任务。
- 更低的资源消耗:相比传统线程,内存占用大大减少。
- 更简单的编程模型:可以像普通线程一样编写逻辑,无需回调或异步复杂处理。
- 适用于非阻塞 I/O 的优化:如 HTTP 请求、数据库访问、文件读取等。
4️⃣ 适用场景
场景 | 描述 |
---|---|
✅ Web 服务器 / REST API 服务 | 处理大量短生命周期请求,提升并发吞吐量 |
✅ 微服务架构 | 支持高并发调用外部服务(如数据库、缓存、远程 API) |
✅ 批量数据抓取 | 同时发起成百上千个网络请求获取数据 |
✅ 实时消息处理 | Kafka、RabbitMQ 等消费端并行处理消息 |
❌ GPU 计算 / 高性能计算(HPC) | 虚拟线程适合 I/O 密集型而非 CPU 密集型任务 |
5️⃣ 使用方式
✅ 基本用法
创建一个虚拟线程:
Thread.ofVirtual()
.name("vt-", 1)
.start(() -> {
System.out.println("Running in virtual thread: " + Thread.currentThread());
});
使用 ExecutorService
提交任务:
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < 1000000; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " running on: " + Thread.currentThread());
return taskId;
});
}
⚠️ 注意:
newVirtualThreadPerTaskExecutor()
返回的ExecutorService
是自动管理生命周期的,但需在适当的时候关闭它:
executor.shutdown();
✅ 与 Spring Boot 整合(基于新版本 Spring Boot 3)
可以将默认的 TaskExecutor
替换为使用虚拟线程的执行器:
@Configuration
public class VirtualThreadConfig {
@Bean
public Executor customTaskExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
}
然后在需要的地方注入并使用:
@Service
public class MyService {
private final Executor executor;
public MyService(Executor executor) {
this.executor = executor;
}
public void asyncOperation() {
executor.execute(() -> {
});
}
}
6️⃣ 性能测试对比
简单的性能测试,比较平台线程与虚拟线程在处理 100000 个任务时的表现:
线程类型 | 任务数 | 平均耗时(ms) | 最大线程数 | 是否 OOM |
---|---|---|---|---|
平台线程(ThreadPool) | 100000 | ~28000 | 100~500 | 否 |
虚拟线程(VTPTE) | 100000 | ~1500 | ~100000 | 否 |
💡 结论:虚拟线程在处理大量并发任务时性能远超平台线程。
7️⃣ 结构化并发
JDK 19 引入了 StructuredTaskScope
,与虚拟线程结合可以构建结构性的并发任务。
并行执行多个子任务
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var future1 = scope.fork(() -> fetchPriceFromServiceA());
var future2 = scope.fork(() -> fetchPriceFromServiceB());
scope.join(); // 等待所有子任务完成
if (scope.isSuccess()) {
System.out.println("Results: " + future1.resultNow() + ", " + future2.resultNow());
} else {
System.err.println("Error occurred: " + future1.exceptionNow());
}
}
✅ 优点:
- 自动管理子任务生命周期
- 更安全的异常处理
- 可读性强,避免“回调地狱”
8️⃣ 注意事项
注意项 | 说明 |
---|---|
不适用于 CPU 密集型任务 | 虚拟线程更适合等待 I/O、网络、数据库等操作 |
不能替代线程池 | 对于计算密集型任务,仍推荐使用平台线程池 |
不可中断(部分情况) | 虚拟线程的中断行为与平台线程略有差异 |
兼容性问题 | 第三方库可能未适配虚拟线程,如某些同步工具、锁机制 |
工具链支持 | IDE(如 IntelliJ IDEA)、监控工具(如 JVisualVM、JConsole)正在逐步增加对 VT 的支持 |
📝 总结
维度 | 推荐程度 |
---|---|
Web API 服务 | ✅✅✅ 推荐全面采用 |
微服务间通信 | ✅✅ 推荐用于异步调用 |
数据抓取 / 批处理 | ✅✅ 推荐 |
实时流处理 | ✅ 推荐用于消费者并行处理 |
高性能数值计算 | ❌ 不推荐 |
GUI 应用 | ⚠️ 视情况而定,不推荐大量使用 |