Java 虚拟线程实战:用同步写法扛住十万并发

495 字
2 分钟
Java 虚拟线程实战:用同步写法扛住十万并发
Java 虚拟线程实战:用同步写法扛住十万并发

为什么需要虚拟线程#

传统的 Java 线程是对操作系统线程的一层薄封装,一个线程大约占用 1MB 栈空间,创建和切换成本都不低。于是我们才有了线程池、回调、CompletableFuture、响应式编程这一整套「为了省线程而牺牲可读性」的方案。

虚拟线程(Virtual Thread,JDK 21 正式)换了个思路:线程由 JVM 调度,成本极低,可以轻松创建几十万个。阻塞时,JVM 会把它从载体线程(carrier thread)上卸下来,让出 CPU。

结论:可以继续用最直观的同步代码,同时拿到接近异步的吞吐。

最小示例#

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 100_000; i++) {
int id = i;
futures.add(executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return "task-" + id;
}));
}
for (var f : futures) {
f.get();
}
}

十万个任务各睡一秒,总耗时依然接近一秒。换成固定大小的平台线程池,你会等很久。

在 Spring Boot 中启用#

Spring Boot 3.2+ 只需要一行配置:

spring:
threads:
virtual:
enabled: true

Tomcat 的请求处理线程、@Async 的执行器都会切换成虚拟线程。

常见误区#

  1. 不要池化虚拟线程。 它们本来就是廉价的,用完即弃。需要限流时用 Semaphore,而不是缩小线程池。
  2. CPU 密集型任务没有收益。 虚拟线程解决的是「等待」问题,不是「计算」问题。
  3. 小心 synchronized 造成的 pinning。 在 JDK 21 中,虚拟线程在 synchronized 块里阻塞会钉住载体线程。能换成 ReentrantLock 就换。可以用 -Djdk.tracePinnedThreads=full 排查。
  4. ThreadLocal 要克制。 线程数量暴涨后,每个线程一份 ThreadLocal 的内存开销会被放大。

小结#

虚拟线程不是银弹,但对大多数 IO 密集的业务后端来说,它能让代码回到最朴素的样子。升级 JDK 21 之后,先把它开在非核心服务上压测一遍,数据会告诉你答案。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Java 虚拟线程实战:用同步写法扛住十万并发
https://www.s9mc.cn/posts/java-virtual-threads/
作者
南柯一梦
发布于
2026-09-28
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
南柯一梦
三年 Java 后端 · 十二年 MC 服主 · AI 驱动开发
公告
欢迎来到南柯一梦!这里记录 Java、Minecraft 服务器与 AI 的折腾日常。
分类
标签
最新动态
站点统计
文章
6
分类
3
标签
17
总字数
2,494
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Vercel
博客版本
v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录
微信二维码
微信扫码添加
QQ:1649721249