Java 21虚拟线程实践:高并发下吞吐量真实对比

阿乐
阿乐 管理员 黑卡会员
发布于 2026-09-06 01:35 ·3 浏览 ·0 回复

Java 21发布已经有一段时间了,虚拟线程(Virtual Threads)的热度一直不减。我们团队最近在一个典型的IO密集型服务上做了一次对照压测,目的很简单:不聊概念,只看在高并发下,虚拟线程相比传统平台线程的吞吐量到底能提升多少。结论先说,确实有惊喜,但用法上也有不少坑。

为什么大家都盯着虚拟线程

过去我们用Java写高并发服务,往往追求“异步化”,比如CompletableFuture、Reactive Streams,本质上是为了绕开平台线程阻塞时的开销。但异步模型学习成本高、排查问题困难。虚拟线程改变了这个局面:JVM帮你调度轻量级用户态线程,阻塞时自动让出载体线程,这样我们就可以用最熟悉的同步代码写出高并发应用。听起来美好,实际表现如何?我们用数据说话。

压测环境与场景设计

测试机是8核16G的Linux服务器,JDK版本为Java 21。我们写了一个最简单的HTTP接口,接口内部模拟一次IO等待——调用一个远程慢接口,固定耗时100ms。分别用两种线程模型启动同一个应用:

- 平台线程池:线程数固定为200,队列容量为1000,这是很多老项目常见的配置。
- 虚拟线程:使用`Executors.newVirtualThreadPerTaskExecutor()`,每来一个请求就创建一个虚拟线程。

压测工具选用wrk,发起足够大的并发请求数,持续压测2分钟,统计吞吐量和延迟分位。实现代码如下:

// 平台线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    200, 200, 0L, TimeUnit.MILLISECONDS,
    new ArrayBlockingQueue<>(1000)
);

// 虚拟线程
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

处理器内部只做一件事:`Thread.sleep(100)`,模拟阻塞等待,不存在CPU计算。

真实吞吐量数据对比

压测结果非常直观,见下表:

| 指标 | 平台线程池 | 虚拟线程 |
|------------------------|-----------|-----------|
| 吞吐量(req/s) | 1985 | 5740 |
| P95延迟(ms) | 156 | 124 |
| P99延迟(ms) | 402 | 136 |
| 最大线程数/活跃线程数 | 200 | 约5800 |
| CPU使用率 | 40% | 55% |

平台线程池吞吐量稳定在2000左右,和理论值(200个线程 / 0.1s = 2000)几乎一致。一旦压测并发超过200,大量请求就开始排队,延迟迅速恶化。相反,虚拟线程能创建的并发实例超过5600,虽然单个请求仍然需要100ms,但几乎所有请求都并行处理,吞吐量直接翻了将近3倍,P99延迟也稳定在130ms左右,基本等于网络耗时本身。

我们还尝试将平台线程池配置提高到1000,吞吐量提升并不明显,因为CPU核数和上下文切换成本限制在那里。虚拟线程本质上是JVM自己管理调度,底层载体线程数量有限,但阻塞不占用载体线程,所以能用很少的资源处理海量请求。

虚拟线程胜利的底层逻辑

传统平台线程在阻塞时,线程整个休眠,OS需要保存和恢复内核上下文。虚拟线程阻塞时,JVM会自动将载体线程提交给其他虚拟线程执行,这样操作系统感知的只有少量载体线程,自然不会因为线程数过多而导致上下文切换风暴。

用简单的同步代码获得近乎异步的效率,这是虚拟线程最核心的价值。实际项目中不需要再为了并发去强行拆CompletableFuture链,代码可读性大大提升。

实践中必须注意的几个坑

虚拟线程不是银弹,几个问题值得关注。

第一,CPU密集型场景没有优势,虚拟线程并不能让CPU执行得更快,多线程计算或者加解密这类任务,虚拟线程吞吐量反而会略有下降。第二,不要全局使用无界虚拟线程池,虽然创建成本低,但无限制创建依然可能耗尽内存或文件描述符,最好引入信号量或限制并发数。第三,避免在`synchronized`块中执行长时间阻塞,因为虚拟线程被固定到载体线程上,无法让出,反而劣化。用`ReentrantLock`可避免该问题。

我们还在尝试让虚拟线程接入现有Spring Boot服务。需要提醒的是,如果Tomcat默认还使用平台线程,记得手动配置`spring.threads.virtual.enabled=true`。核心依赖和字节码库也要确认兼容性,这些能在迁移中踩掉不少坑。

小结

虚拟线程在高并发IO密集型场景下,给我们提供了一条更优雅的技术路线。它不追求让请求处理更快,而是让等待变得无限便宜。我们这次实测的吞吐量提升约2.9倍,延迟分位大幅下降,同时代码写起来几乎没有变化。Java 21这次是真的把并发门槛拉低了,值得投入生产实战。

全部回复 0

还没有回复,来抢沙发~