Java 21 虚拟线程实战:百万并发连接的轻量解决方案
David Ng | 2026-08-26T23:01:54 | Java
详解 Java 21 Virtual Thread 的原理和使用方式,通过 HTTP 服务器案例演示百万并发的可行性。
# Java 21 虚拟线程实战 ## 传统线程的瓶颈 每个 Java 平台线程对应一个 OS 线程,占用约 1MB 栈内存。10000 个并发连接就需要 10GB 内存。 ## 虚拟线程的优势 Virtual Thread 是 JVM 层面的轻量级线程,由 JVM 调度到少量 carrier thread 上执行。内存占用仅几 KB。 ## 基本用法 ```java // 方式一:直接创建 Thread vt = Thread.ofVirtual().name("worker-1").start(() -> { System.out.println("当前线程: " + Thread.currentThread()); }); // 方式二:ExecutorService try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { for (int i = 0; i { Thread.sleep(Duration.ofSeconds(1)); return "done"; }); } } // 10 万个虚拟线程并发运行,内存占用不到 1GB ``` ## Spring Boot 集成 ```yaml # application.yml spring: threads: virtual: enabled: true # Spring Boot 3.2+ 一行配置 ``` 这会将 Tomcat 的请求处理线程替换为虚拟线程。 ## 性能对比 ```java // 压测代码 long start = System.nanoTime(); try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { List> futures = new ArrayList(); for (int i = 0; i { Thread.sleep(Duration.ofMillis(100)); return "ok"; })); } for (var f : futures) f.get(); } long elapsed = (System.nanoTime() - start) / 1_000_000; System.out.println("耗时: " + elapsed + "ms"); ``` | 方案 | 10 万任务耗时 | 内存峰值 | |------|-------------|---------| | 固定线程池(200) | 50 秒 | 300MB | | 虚拟线程 | 0.5 秒 | 800MB | ## 注意事项 1. **不要池化虚拟线程**:虚拟线程极其轻量,用完即弃 2. **避免 synchronized**:改用 `ReentrantLock`,因为 synchronized 会 pin 住 carrier thread 3. **ThreadLocal 谨慎使用**:百万虚拟线程意味着百万份 ThreadLocal 副本 ```java // 避免 synchronized // 错误: synchronized(lock) { doIO(); } // 正确: ReentrantLock lock = new ReentrantLock(); lock.lock(); try { doIO(); } finally { lock.unlock(); } ```