2026.07.25
ARTICLE
Java 虚拟线程落地指南:从能运行到适合生产
理解 Java 虚拟线程适合解决的问题、常见误区,以及在 Spring Boot 服务中的生产落地方式。
虚拟线程最容易被误解成“更快的线程”。准确地说,它解决的是传统平台线程成本较高的问题,让一个进程能够承载大量以等待为主的并发任务。它不会让 CPU 密集型计算凭空提速,也不会替我们消除数据库连接数、下游限流和共享资源竞争。
为什么传统线程容易成为瓶颈
在典型的 Web 服务中,一个请求往往要等待数据库、Redis 或第三方接口。传统“一请求一线程”模型简单直观,但平台线程与操作系统线程关系紧密,数量增加后会带来更高的内存占用和上下文切换成本。
异步编程能提高资源利用率,却会引入回调、响应式操作符和上下文传播等额外复杂度。虚拟线程保留了同步代码的写法:任务阻塞时,JVM 可以暂时卸载承载它的平台线程,让平台线程继续执行其他任务。
一个最小示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import java.util.concurrent.Executors;
public class VirtualThreadDemo {
public static void main(String[] args) throws Exception { try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { for (int i = 0; i < 10_000; i++) { int taskId = i; executor.submit(() -> fetchRemoteData(taskId)); } } }
private static String fetchRemoteData(int taskId) { try { Thread.sleep(100); return "task-" + taskId; } catch (InterruptedException exception) { Thread.currentThread().interrupt(); throw new IllegalStateException("任务被中断", exception); } } }
|
这里创建一万个任务并不等于系统可以无条件承受一万个外部请求。虚拟线程降低的是线程成本,不是下游资源成本。
生产环境必须保留并发边界
数据库连接池只有 50 个连接时,同时发起 5000 次查询,只会让大量任务排队。更危险的是,下游接口可能在突发流量下被直接压垮。因此,应在稀缺资源前设置明确的并发上限。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import java.util.concurrent.Semaphore;
public final class LimitedRemoteClient {
private final Semaphore permits = new Semaphore(100);
public String call() { boolean acquired = false; try { permits.acquire(); acquired = true; return invokeRemoteService(); } catch (InterruptedException exception) { Thread.currentThread().interrupt(); throw new IllegalStateException("远程调用被中断", exception); } finally { if (acquired) { permits.release(); } } }
private String invokeRemoteService() { return "ok"; } }
|
警惕线程固定
虚拟线程在某些场景下无法从平台线程卸载,这通常被称为 pinning。长期持有 synchronized 锁并在锁内执行阻塞 I/O,是需要重点排查的模式。应缩短临界区,避免在锁中调用数据库或远程接口,并通过 JFR 观察虚拟线程固定事件。
ThreadLocal 也需要谨慎。虚拟线程可以很多,如果每个线程都保存大对象,内存仍会快速增长。请求上下文应尽量小,并在生命周期结束后清理。
什么时候值得使用
虚拟线程适合大量相互独立、以阻塞 I/O 为主、希望继续采用同步编程模型的任务。对于纯计算任务,线程数通常仍应接近 CPU 核数;对于已经稳定运行的响应式系统,也没有必要仅为了追逐新特性而重写。
上线前至少压测三组指标:吞吐量与尾延迟、堆内存与平台线程数量、数据库和下游接口的排队情况。真正可靠的结论来自系统整体,而不是“创建了多少虚拟线程”。
总结
虚拟线程让并发代码重新变得直接,但它不是无限并发许可证。将它用于 I/O 密集任务,保留连接池、信号量、超时和限流等边界,再配合 JFR 与压力测试验证,才是从“能运行”走向“适合生产”的关键。