虚拟线程
虚拟线程是 Java 21 正式引入的平台能力,用于以更低成本承载大量并发阻塞任务。它不是更快的 CPU 计算线程,而是让“一个请求一个线程”的阻塞式编程模型具备更好的扩展性。
# 1. 平台线程与虚拟线程
平台线程通常对应操作系统线程。
Java 平台线程
│
▼
OS Thread
│
▼
CPU 调度
虚拟线程由 JVM 调度,运行时挂载到少量平台线程上执行。
虚拟线程 1 ┐
虚拟线程 2 ├─ JVM 调度 -> 平台线程 -> OS Thread
虚拟线程 N ┘
虚拟线程数量可以远大于平台线程数量。
# 2. 适合什么场景
适合:
- 大量阻塞 IO。
- Web 请求处理。
- RPC/HTTP 调用。
- 数据库访问。
- 代码以同步阻塞风格编写,但需要更高并发。
不适合:
- CPU 密集型计算。
- 需要限制下游资源但没有限流。
- 依赖大量 ThreadLocal 大对象。
- 长时间持有锁并阻塞。
虚拟线程提高的是并发承载能力,不会让 CPU 密集型计算突破 CPU 核心限制。
# 3. 创建虚拟线程
Thread.startVirtualThread(() -> {
handleRequest();
});
或:
Thread.ofVirtual()
.name("vt-", 0)
.start(this::handleRequest);
使用 Executor:
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(this::handleRequest);
}
一个任务一个虚拟线程是常见模型。
# 4. 阻塞时发生什么
传统平台线程阻塞:
线程执行 IO
│
▼
OS Thread 阻塞
│
▼
占用平台线程资源
虚拟线程阻塞:
虚拟线程执行可挂起阻塞操作
│
▼
虚拟线程卸载
│
▼
平台线程去执行其他虚拟线程
这让大量阻塞任务不再需要同等数量的平台线程。
# 5. 虚拟线程不是线程池调参神器
虚拟线程很便宜,但外部资源不便宜。
10 万个虚拟线程
│
├─ 数据库连接池只有 50
├─ 下游接口限流 100 QPS
└─ CPU 核心只有 8
仍然需要:
- 数据库连接池。
- 限流。
- 超时。
- 熔断。
- 队列或信号量控制。
不要因为虚拟线程轻量,就无限制打下游。
# 6. synchronized 与 pinning
虚拟线程在某些情况下可能被固定在平台线程上,例如在 synchronized 块中执行某些阻塞操作时。
不推荐:
synchronized (lock) {
remoteCall(); // 慢 IO
}
建议:
- 不要在锁内做慢 IO。
- 使用更细粒度锁。
- 必要时使用
ReentrantLock。 - 通过监控观察虚拟线程 pinning。
# 7. ThreadLocal 注意点
虚拟线程数量可能非常大,如果每个虚拟线程都放大对象到 ThreadLocal,会带来明显内存压力。
虚拟线程数量巨大
│
▼
每个线程 ThreadLocal 持有大对象
│
▼
内存压力上升
建议:
- 少用大对象 ThreadLocal。
- 及时 remove。
- 优先显式传递上下文。
# 8. 与线程池关系
平台线程池需要控制线程数量。虚拟线程通常不需要池化虚拟线程本身。
Executors.newVirtualThreadPerTaskExecutor()
每个任务创建一个新的虚拟线程,任务结束线程也结束。虚拟线程创建成本低,池化意义不大。
但仍要控制任务入口速率和外部资源并发。
# 9. 迁移建议
- 优先迁移阻塞 IO 多、线程数压力大的服务。
- 保留同步代码风格,减少回调复杂度。
- 给所有外部 IO 设置超时。
- 检查锁内阻塞。
- 检查 ThreadLocal 使用。
- 压测并观察吞吐、延迟、内存、下游压力。
# 10. 虚拟线程与 CompletableFuture
CompletableFuture 适合异步编排,虚拟线程适合用同步阻塞风格承载大量并发。
| 场景 | 选择 |
|---|---|
| 多任务异步组合 | CompletableFuture |
| 大量阻塞请求处理 | 虚拟线程 |
| CPU 并行计算 | ForkJoin/普通线程池 |
| 复杂响应式链路 | 响应式框架或异步编排 |
虚拟线程不是消灭异步编程,而是让阻塞式代码在很多 IO 场景下重新变得可扩展。
# Tips 快问快答
Q:虚拟线程是 Java 哪个版本正式引入的? A:Java 21。
Q:虚拟线程适合 CPU 密集型任务吗? A:不适合提升 CPU 上限,CPU 密集型仍受核心数限制。
Q:虚拟线程需要池化吗? A:通常不需要,一个任务一个虚拟线程即可。
Q:用了虚拟线程还需要限流吗? A:需要。虚拟线程轻量,不代表数据库和下游服务无限。
Q:虚拟线程会自动解决锁竞争吗? A:不会,锁竞争和共享状态问题仍然存在。
Q:ThreadLocal 在虚拟线程中能用吗? A:能用,但要谨慎,数量巨大时内存压力明显。
Q:虚拟线程能替代 CompletableFuture 吗? A:不能完全替代。虚拟线程适合同步阻塞风格,CompletableFuture 适合异步组合。
Q:迁移虚拟线程最该检查什么? A:外部 IO 超时、锁内阻塞、ThreadLocal 和下游容量。