Java 22 的虚拟线程(Virtual Threads)是 Project Loom 正式落地的轻量级线程,由 JVM 调度、不绑定 1:1 操作系统线程,主打极低内存开销、百万级并发、同步编程模型。下面从概念、对比、用法、示例、适用场景与避坑一次性讲清楚。


一、什么是虚拟线程

  • 平台线程(传统 Thread):1:1 映射 OS 线程,栈默认 1MB,创建 / 切换开销高,数量受限(通常几千)。
  • 虚拟线程(Virtual Thread)
    • JVM 用户态调度,挂载在少量平台线程(载体线程)上。
    • 栈内存KB 级,创建开销接近零,可轻松创建百万级
    • 阻塞(如 sleep、I/O、JDBC)时自动让出载体线程,不阻塞 OS 线程,吞吐量暴涨。

简单说:虚拟线程 = 轻量、海量、不占 OS 线程的 “用户态线程”


二、虚拟线程 vs 平台线程(Java 22)

表格

特性虚拟线程平台线程
调度方JVM(用户态)OS(内核态)
栈内存~200KB(可更小)~1MB
创建开销极低(对象分配级)高(OS 资源)
最大数量百万级(无硬限)数千(OS 限制)
阻塞影响不阻塞载体线程阻塞 OS 线程
适用场景I/O 密集(DB、HTTP、RPC)CPU 密集、临界区

三、Java 22 核心用法(直接可运行)

1)直接创建虚拟线程

java

运行

// 方式1:创建并启动
Thread vt1 = Thread.ofVirtual()
    .name("vt-", 0) // 命名前缀+自增
    .start(() -> {
        System.out.println("虚拟线程运行:" + Thread.currentThread());
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
    });

// 方式2:先创建、后启动
Thread vt2 = Thread.ofVirtual().unstarted(() -> { ... });
vt2.start();
vt2.join(); // 等待完成
2)虚拟线程执行器(推荐,替代线程池)

Executors.newVirtualThreadPerTaskExecutor()每个任务一个虚拟线程,无需池化

java

运行

import java.util.concurrent.Executors;

public class VtDemo {
    public static void main(String[] args) {
        // 自动关闭(try-with-resources)
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            // 提交 10 万任务,轻松运行
            for (int i = 0; i < 100_000; i++) {
                int id = i;
                executor.submit(() -> {
                    // 模拟 I/O 阻塞(DB/HTTP)
                    try { Thread.sleep(200); } catch (InterruptedException e) {}
                    System.out.println("任务 " + id + " 完成");
                });
            }
        }
        // executor 关闭后等待所有虚拟线程结束
    }
}
3)结构化并发(Java 22 增强,推荐)

StructuredTaskScope 管理虚拟线程生命周期,父线程等待所有子线程完成,失败自动取消

java

运行

import java.util.concurrent.StructuredTaskScope;

public class StructuredVt {
    public static void main(String[] args) throws Exception {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            // 提交多个虚拟线程任务
            var task1 = scope.fork(() -> {
                Thread.sleep(500);
                return "结果1";
            });
            var task2 = scope.fork(() -> {
                Thread.sleep(800);
                return "结果2";
            });

            scope.join(); // 等待所有任务完成
            scope.throwIfFailed(); // 任一失败则抛异常

            System.out.println(task1.get() + ", " + task2.get());
        }
    }
}

四、Java 22 相比 21 的关键改进

  • 调度器重写:工作窃取算法,并发能力提升约 30%
  • ThreadLocal 优化:虚拟线程中使用 ThreadLocal 性能显著提升。
  • 监控增强:JFR 支持虚拟线程事件(创建、挂起、恢复)。
  • 结构化并发稳定StructuredTaskScope 正式转正,更安全易用。

五、适用场景(一句话判断)

  • I/O 密集型(首选):Web 请求、DB 查询、HTTP 调用、RPC、消息消费。
  • 高并发微服务:Spring Boot 3.2+、Tomcat 10+ 可直接启用虚拟线程处理请求。
  • CPU 密集型:虚拟线程不加速计算,用平台线程池 + 并行流更合适。
  • 频繁同步锁 / 竞争:虚拟线程阻塞在锁上仍会占用载体线程,需优化锁粒度。

六、避坑指南(生产必看)

  1. 不要池化虚拟线程newVirtualThreadPerTaskExecutor 是 “每个任务一线程”,池化无意义且复杂。
  2. 避免在虚拟线程中阻塞平台线程池

    java

    运行

    // 反例:虚拟线程内调用固定平台线程池,会被池大小限制
    ExecutorService platformPool = Executors.newFixedThreadPool(10);
    try (var vtExec = Executors.newVirtualThreadPerTaskExecutor()) {
        vtExec.submit(() -> platformPool.submit(() -> {})); // 瓶颈
    }
    
  3. 慎用 ThreadLocal:虽优化,但百万虚拟线程下仍可能内存泄漏,优先用作用域变量(ScopedValue,Java 22 新)
  4. 监控虚拟线程

    bash

    运行

    # 查看虚拟线程数、载体线程数
    jcmd <pid> Thread.print -l
    

七、快速迁移(老项目改造三步)

  1. 升级 JDK 到 22,框架到 Spring Boot 3.2+/Tomcat 10+。
  2. 替换线程池:

    java

    运行

    // 旧:固定线程池
    ExecutorService oldPool = Executors.newFixedThreadPool(200);
    // 新:虚拟线程执行器
    ExecutorService vtPool = Executors.newVirtualThreadPerTaskExecutor();
    
  3. 启用 Web 虚拟线程(Spring Boot):

    properties

    # application.properties
    spring.threads.virtual.enabled=true
    

总结

Java 22 虚拟线程是高并发 I/O 场景的最优解:用同步代码写异步逻辑,百万级并发不宕机,内存开销极低。核心是 **Thread.ofVirtual() + newVirtualThreadPerTaskExecutor() + 结构化并发 **。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐