虚拟线程实现百万级吞吐的核心是I/O阻塞时不卡载体线程,依赖调度器+轻量栈+自动挂起恢复机制;正确使用newVirtualThreadPerTaskExecutor(),避免套入传统线程池,同步代码即可获得异步性能。

直接用虚拟线程压榨单机百万级吞吐,核心不是“堆更多线程”,而是让 I/O 阻塞不卡住底层平台线程。JDK 21 的虚拟线程本质是调度器+轻量栈+自动挂起恢复的组合,关键在用对方式、避开陷阱。
选对执行器:别包装,直接用 newVirtualThreadPerTaskExecutor()
虚拟线程不是用来放进传统线程池的——那是典型误用。Executors.newFixedThreadPool 或 newCachedThreadPool 包裹虚拟线程,会抵消全部优势,甚至更差。
- ✅ 正确做法:用 Executors.newVirtualThreadPerTaskExecutor(),它返回一个专为虚拟线程设计的 ExecutorService,内部自动绑定 ForkJoinPool 作为载体调度器
- ✅ Spring Boot 3.2+ 可替换 Tomcat 线程模型:
@Bean public TomcatProtocolHandlerCustomizer<?> protocolHandlerCustomizer() {
return handler -> handler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
} - ❌ 错误示例:
ExecutorService pool = Executors.newFixedThreadPool(100);
pool.submit(Thread.ofVirtual().unstarted(() -> {...})); // 白费力气
写同步代码,但享受异步吞吐
不用改业务逻辑风格。HTTP 调用、数据库查询(配合支持虚拟线程的驱动)、文件读写、Thread.sleep() —— 全部照常写阻塞式代码。JVM 在遇到阻塞点(如 socket.read、Object.wait、LockSupport.park)时,自动将虚拟线程挂起,把载体线程腾出来跑别的任务。
- 例如:一个请求需串行调用 3 个下游 HTTP 接口,每个平均耗时 200ms,传统 200 线程池最多并发 200 请求;换成虚拟线程后,单机可轻松维持 10 万+ 并发连接,QPS 翻倍甚至更高
- 注意:确保所用客户端库支持虚拟线程挂起(如 JDK 自带 HttpClient、Spring WebClient 6.1+、PostgreSQL JDBC 42.7+)
- 避免在虚拟线程中做纯 CPU 密集型长任务(如大数组排序、加密计算),会独占载体线程,拖慢整体调度。真有需要,应显式提交到专用的 CPU 密集型线程池
调优载体线程池:默认够用,极端场景可微调
虚拟线程默认复用 ForkJoinPool.commonPool(),并行度等于 CPU 核心数。大多数 I/O 密集型服务无需调整,但若压测发现 CPU 利用率偏低而吞吐未达预期,说明载体线程成了瓶颈。
- 可通过 JVM 参数控制:
-Djdk.virtualThreadScheduler.parallelism=64
-Djdk.virtualThreadScheduler.maxPoolSize=128
(建议设为物理核心数的 1–2 倍,避免过度竞争) - 观察指标:用 jcmd 或 JFR 查看 jdk.VirtualThreadParked 和 jdk.VirtualThreadUnparked 事件频次,高频率说明挂起/恢复正常;若 jdk.ThreadStart 持续飙升但吞吐不上,可能是创建过载或 GC 压力大
- 内存方面:虚拟线程初始栈仅几百字节,堆内分配,GC 可回收。只要不泄漏(比如把虚拟线程对象长期存入静态 Map),512GB 内存跑百万虚拟线程毫无压力
压测与验证:用真实流量模式,别只看 ab
ab 或 wrk 单一路径压测容易掩盖问题。真实百万吞吐依赖的是“长连接 + 多阶段 I/O + 适度并发”的混合负载。
- 推荐组合:用 ghz(gRPC)或 k6(HTTP)模拟多用户、多路径、带思考时间的流量,观察 P99 延迟是否稳定在 10–30ms 区间
- 重点监控:
• JVM 线程总数(jstack | grep "virtual" | wc -l)应远高于平台线程数(通常 • ZGC 停顿时间(启用 -XX:+UseZGC)应稳定在 sub-10ms 级别
• 网络连接数(ss -s)和 TIME_WAIT 数量,确认没被本地端口耗尽 - 一次有效压测配置示例:
k6 run --vus 50000 --duration 5m script.js
脚本中每个 VU 模拟一个用户,含登录 → 查询 → 下单 → 支付四步,每步含随机 50–200ms 等待

















