Java 虚拟线程实战:用 Project Loom 重写高并发服务

Java Virtual Threads in Action: Rewriting High-Concurrency Services with Project Loom

| Kevin | 2026-08-31T09:27:17

Java 21 的虚拟线程终于稳定了,我们把一个高并发的 IO 密集型服务从线程池模式切换到了虚拟线程,效果出奇地好。

Java 21 virtual threads are stable. We migrated a high-concurrency IO-intensive service from thread pools to virtual threads with impressive results.

Java 21 正式发布了虚拟线程(Virtual Threads),之前在预览版里试过,感觉很有潜力。最近终于找到一个合适的场景来试水了。 场景描述 我们有一个数据聚合服务,每次请求需要同时调用 5-8 个下游 API,然后聚合结果返回。之前用的传统线程池,配了 200 个线程,高峰期经常不够用。 传统线程池的问题 // 之前的做法:线程池 + CompletableFuture ExecutorService executor = Executors.newFixedThreadPool(200); List<CompletableFuture<ApiResult>> futures = apis.stream() .map(api -> CompletableFuture.supplyAsync(() -> callApi(api), executor)) .toList(); CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); 200 个线程,每个线程栈默认 1MB,光线程就要占 200MB 内存。而且这些线程大部分时间都在等 IO(HTTP 调用),CPU 利用率很低。 切换到虚拟线程 // 现在的做法:虚拟线程 try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { List<Future<ApiResult>> futures = apis.stream() .map(api -> executor.submit(() -> callApi(api))) .toList(); List<ApiResult> results = futures.stream() .map(f -> { try { return f.get(); } catch (Exception e) { return ApiResult.empty(); } }) .toList(); } 代码改动量非常小,就是把 newFixedThreadPool 换成 newVirtualThreadPerTaskExecutor。虚拟线程的栈空间只有几 KB,可以轻松创建上万个。 Spring Boot 3.2+ 的支持 如果用 Spring Boot 3.2+,一行配置就能启用虚拟线程: spring: threads: virtual: enabled: true 这样 Tomcat 的每个请求处理都会用虚拟线程,不再需要手动管理线程池。 性能对比 指标平台线程 200虚拟线程 最大并发请求20010000+ P99 延迟2.3s850ms 内存占用1.2GB400MB CPU 利用率15%35% 注意事项 虚拟线程不适合 CPU 密集型任务,那种场景还是用平台线程 如果代码里有 synchronized 块,虚拟线程可能会被 pin 住(不能让出),尽量换成 ReentrantLock ThreadLocal 要谨慎使用,虚拟线程数量可能很多,ThreadLocal 的内存开销会放大


Migrated an IO-intensive data aggregation service from 200-thread pool to Java 21 virtual threads. Before vs After Code change was minimal: replace newFixedThreadPool(200) with newVirtualThreadPerTaskExecutor(). Spring Boot 3.2+ supports it with one config line. Results Max concurrent requests: 200 → 10000+. P99 latency: 2.3s → 850ms. Memory: 1.2GB → 400MB. Caveats Not for CPU-intensive work. Watch for synchronized blocks (pin virtual threads) and ThreadLocal memory amplification.

← Back to News