
Java 21 是继 Java 17 之后的新一代长期支持版本LTS。在 Java 21 的众多新特性中最值得我们求职者关注的就是虚拟线程Virtual Thread。虚拟线程并不是一种让 CPU 计算变快的“超级线程”它主要解决的是在大量请求都需要等待数据库、网络接口等 I/O 操作时传统线程数量不够用、创建成本又太高的问题。本文先用人话解释虚拟线程为什么出现、它是怎么运行的然后再介绍具体用法和注意事项。最后一笔带过Java 21 的其他常用特性。一、先理解三种线程学习虚拟线程之前需要先分清操作系统线程、平台线程和虚拟线程。1.操作系统线程操作系统线程是由 Windows、Linux 等操作系统直接管理的线程。它的特点是“比较重”创建和销毁需要操作系统参与。每个线程都要占用一定内存。线程切换需要进入操作系统内核存在性能成本。单个进程能够创建的线程数量有限具体上限受内存、操作系统配置等因素影响。我们可以把操作系统线程理解成饭店里的真实服务员。招聘一个服务员需要成本饭店也不可能同时雇佣几十万个服务员。2.平台线程在以前的 Java 中通过new Thread()创建的线程通常是平台线程Thread thread new Thread(() - { System.out.println(执行任务); }); thread.start();平台线程由 JVM 提供给 Java 程序使用但底层通常对应一个操作系统线程Java 平台线程 ↓ 操作系统线程因此平台线程实际上是 JVM 对操作系统线程的一层包装。操作系统线程比较昂贵平台线程自然也不能无限创建。传统项目通常会创建线程池让少量平台线程反复处理任务ExecutorService pool Executors.newFixedThreadPool(200);相当于饭店只雇佣 200 个服务员让大量订单排队等待这 200 个服务员处理。3.虚拟线程虚拟线程是由 JVM 管理的轻量级线程在 JDK 19 中首次以预览特性出现并在 JDK 21 中正式发布。它和平台线程最大的区别是传统方式一个平台线程通常对应一个操作系统线程 虚拟线程大量虚拟线程复用少量平台线程可以把它们理解成虚拟线程一张订单平台线程真正处理订单的服务员JVM负责派发订单的经理操作系统线程服务员实际占用的工作岗位订单可以有几十万张但并不需要几十万个服务员。JVM 会安排少量平台线程轮流执行大量虚拟线程。二、为什么需要虚拟线程1.传统“一请求一线程”模型的问题传统 Web 服务器经常采用“一次请求对应一个线程”的处理模型请求 1 → 平台线程 1 请求 2 → 平台线程 2 请求 3 → 平台线程 3这种模型很好理解代码也容易编写。但平台线程数量有限当请求量很大时线程资源可能成为系统瓶颈。更麻烦的是大部分后端请求并不是一直在使用 CPU而是在等待接收请求 ↓ 查询数据库 ↓ 等待数据库返回 ← 大部分时间可能花在这里 ↓ 处理查询结果 ↓ 返回响应假设一次数据库查询需要等待两秒。使用传统平台线程时这个线程可能会被当前请求占用两秒但在等待期间它并没有真正执行多少计算。如果同时有大量请求都在等待数据库或远程接口就会出现大量平台线程处于阻塞等待状态。线程占用了内存却没有充分使用 CPU。请求继续增加后只能在线程池队列里排队。线程过多还会增加操作系统的调度和切换成本。2.虚拟线程如何解决虚拟线程执行到可识别的阻塞 I/O 操作时JVM 可以暂时把它从当前平台线程上卸载下来。虚拟线程 A发起数据库查询需要等待 ↓ JVM 保存虚拟线程 A 当前执行到哪里 ↓ 平台线程空出来开始执行虚拟线程 B ↓ 数据库结果返回后再继续执行虚拟线程 A用饭店来比喻服务员把菜单交给厨房后不需要站在厨房门口一直等。他可以先去处理其他订单等厨房做好以后再回来继续处理原来的订单。因此少量平台线程可以支撑大量正在等待 I/O 的虚拟线程。整体关系大致如下虚拟线程 1 ─┐ 虚拟线程 2 ─┤ 虚拟线程 3 ─┼──→ 少量平台线程 ──→ 少量操作系统线程 虚拟线程 4 ─┤ 虚拟线程 5 ─┘这里负责实际执行虚拟线程的平台线程也经常被称为载体线程Carrier Thread。三、虚拟线程是如何切换的线程要想暂停后继续运行系统必须知道它刚才执行到了哪里并保存相关数据例如方法调用到了哪一层方法中的局部变量下一条要执行的指令位置这些信息可以简单理解为线程的“工作现场”。当虚拟线程因为 I/O 等待而暂停时JVM 会保存它的工作现场让平台线程去执行其他虚拟线程等条件满足后JVM 再恢复这个虚拟线程。因此虚拟线程的切换主要由 JVM 在用户态完成不需要每次都让操作系统切换一个重量级线程成本通常更低。不过需要注意虚拟线程只是共享平台线程并不是所有虚拟线程都能在同一时刻并行运行。真正同时运行多少任务最终仍然受到 CPU 核心数和平台线程数量限制。虚拟线程的优势是让平台线程在其他任务等待 I/O 时不闲着。四、如何创建虚拟线程Java 21 提供了多种创建虚拟线程的方式。1.直接启动虚拟线程public class VirtualThreadDemo { public static void main(String[] args) throws InterruptedException { Thread thread Thread.startVirtualThread(() - { System.out.println(当前线程 Thread.currentThread()); }); thread.join(); } }Thread.startVirtualThread()会创建并立即启动一个虚拟线程。2.使用Thread.ofVirtual()public class VirtualThreadBuilderDemo { public static void main(String[] args) throws InterruptedException { Thread thread Thread.ofVirtual() .name(order-task) .start(() - System.out.println(处理订单)); thread.join(); } }这种方式可以为虚拟线程设置名称方便日志记录和问题排查。3.使用虚拟线程执行器在实际项目中更常见的方式是使用Executors.newVirtualThreadPerTaskExecutor()import java.util.concurrent.Executors; import java.util.stream.IntStream; public class VirtualThreadExecutorDemo { public static void main(String[] args) { try (var executor Executors.newVirtualThreadPerTaskExecutor()) { IntStream.range(0, 10_000).forEach(i - executor.submit(() - { Thread.sleep(1_000); System.out.println(任务 i 执行完成); return i; }) ); } } }这段代码会为每个任务创建一个虚拟线程。虽然提交了 10,000 个任务但并不意味着操作系统会创建 10,000 个线程。虚拟线程执行器实现了AutoCloseable离开try代码块时会等待已提交任务结束。五、平台线程和虚拟线程的区别对比项平台线程虚拟线程管理者操作系统为主JVM 为主与操作系统线程的关系通常近似一对一多个虚拟线程复用少量平台线程创建成本较高较低内存占用较大较小并可按需增长可创建数量相对有限可以创建非常多是否需要线程池复用通常需要通常不需要适合场景通用尤其是固定数量的计算任务大量并发、阻塞式 I/O 任务这里的“可以创建非常多”不等于完全没有成本。每个虚拟线程仍然需要保存自己的状态创建数量最终仍受可用内存等资源限制。六、虚拟线程适合哪些场景虚拟线程特别适合任务数量多而且任务经常等待 I/O 的场景。适合的场景Web 请求处理JDBC 数据库查询HTTP、RPC 远程调用文件读写消息消费大量需要等待结果的独立任务例如一个订单接口需要依次查询数据库、调用库存服务和支付服务大部分时间都在等待外部结果这种场景很适合虚拟线程。不会明显受益的场景视频编码图片压缩密码计算大规模数学运算其他长时间占用 CPU 的任务如果任务一直在计算没有阻塞等待虚拟线程就没有机会卸载并让出平台线程。例如一台机器只有 8 个 CPU 核心即使创建 10 万个虚拟线程执行纯计算也不可能让 10 万个任务真正同时计算。任务反而可能因为频繁调度而变慢。所以要记住虚拟线程解决的是“线程被大量等待占住”的问题不是“CPU 不够快”的问题。七、虚拟线程常见误区误区 1虚拟线程比普通线程执行得更快不准确。执行同一段纯计算代码时虚拟线程不一定更快。它的优势是等待 I/O 时可以释放载体线程从而提高系统吞吐量。误区 2有了虚拟线程就不需要数据库连接池不正确。数据库连接本身是昂贵且有限的资源仍然需要连接池管理。误区 3虚拟线程数量没有上限不正确。虚拟线程很轻但仍然会占用内存和 JVM 管理资源只是可创建数量通常远高于平台线程。误区 4虚拟线程等于异步编程不完全相同。虚拟线程允许开发者继续使用容易理解的同步阻塞代码User user userService.findById(id); ListOrder orders orderService.findByUserId(id); return new UserDetail(user, orders);代码看起来仍然是从上往下执行的但线程在等待 I/O 时JVM 可以在底层调度其他虚拟线程。它的价值之一就是在获得高并发能力的同时避免把业务代码写成大量回调或复杂的异步调用链。八、Java 21 的其他常用新特性除了虚拟线程Java 21 还有几个值得了解的正式特性。1.switch模式匹配可以直接根据对象类型进行分支判断static String format(Object value) { return switch (value) { case Integer i - 整数 i; case String s - 字符串 s; case null - 空值; default - 其他类型; }; }相比连续使用多个if instanceof代码更加清晰。2. 记录模式可以直接拆解record中的数据record Point(int x, int y) {} static void print(Object value) { if (value instanceof Point(int x, int y)) { System.out.println(x x , y y); } }3. 顺序集合Java 21 新增了SequencedCollection、SequencedSet和SequencedMap统一提供首元素、尾元素和反向视图等操作ListString list new ArrayList(List.of(A, B, C)); list.addFirst(Start); list.addLast(End); System.out.println(list.getFirst()); System.out.println(list.getLast()); System.out.println(list.reversed());4. 分代 ZGCJava 21 为 ZGC 引入分代回收能力。它根据“大多数新对象存活时间很短”的特点对年轻对象和长期存活对象分别管理从而降低部分场景下的垃圾回收成本。Java 21 中可以通过以下参数启用java -XX:UseZGC -XX:ZGenerational MyApplication是否切换垃圾收集器应以真实业务压测结果为准。九、总结虚拟线程的核心思想其实就是让大量轻量级虚拟线程复用少量重量级平台线程。当一个虚拟线程等待 I/O 时平台线程可以先去执行其他虚拟线程。最后总结一下平台线程通常接近一个 Java 线程对应一个操作系统线程创建成本较高。虚拟线程由 JVM 管理大量虚拟线程可以复用少量平台线程。虚拟线程适合大量数据库、网络、文件等阻塞 I/O 任务。虚拟线程不会让纯 CPU 计算变快也不会扩大数据库和下游服务的容量。对于传统的“一请求一线程”Java Web 应用虚拟线程降低了高并发编程的门槛。开发者可以继续使用直观的同步代码同时减少大量平台线程阻塞等待造成的资源浪费。这也是虚拟线程成为 Java 21 最重要新特性之一的原因。最后如果你觉得本篇内容对你有帮助的话欢迎点赞关注评论谢谢⭐⭐⭐