news 2026/4/29 7:07:03

jdk 21虚拟线程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
jdk 21虚拟线程

什么是虚拟线程?

在 Java 21 之前,Java 的Thread是对操作系统线程(也称为平台线程)的一层薄封装,两者是 1:1 的关系。这意味着:

  • 资源开销大:每个平台线程需要占用约 1MB 的栈内存,创建数千个线程就可能耗尽系统内存。
  • 调度成本高:线程的创建、销毁和上下文切换都由操作系统内核完成,成本高昂。

虚拟线程则完全不同:

  • 轻量级:它是由 JVM 管理和调度的用户态线程,不直接对应操作系统线程。
  • M:N 模型:大量的虚拟线程(M)会被复用到少量的平台线程(N,也称为载体线程/Carrier Thread)上执行。
  • 成本极低:创建一个虚拟线程仅消耗几百字节的内存,可以轻松创建百万级别的虚拟线程。

虚拟线程 vs. 平台线程

表格

特性平台线程 (Platform Thread)虚拟线程 (Virtual Thread)
调度者操作系统 (OS)Java 虚拟机 (JVM)
内存开销高 (默认 ~1MB 栈空间)极低 (初始几百字节,动态伸缩)
最大数量有限 (通常几千个)极高 (百万级)
适用场景CPU 密集型任务I/O 密集型任务
阻塞行为阻塞整个 OS 线程,资源浪费自动挂起,释放载体线程给其他任务

如何使用虚拟线程

使用虚拟线程非常简单,主要有以下几种方式:

1. 使用ThreadAPI 直接创建
// 方式一:启动一个虚拟线程 Thread vThread = Thread.startVirtualThread(() -> { System.out.println("Hello from virtual thread: " + Thread.currentThread()); }); // 方式二:使用 Builder 模式 Thread vThread2 = Thread.ofVirtual() .name("my-virtual-thread") .unstarted(() -> { // 你的任务逻辑 }); vThread2.start();
2. 使用 ExecutorService (推荐)

这是与现有线程池代码集成最方便的方式。

// 为每个任务创建一个虚拟线程的 ExecutorService try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { for (int i = 0; i < 10_000; i++) { executor.submit(() -> { // 模拟耗时 I/O 操作 Thread.sleep(1000); return "任务完成"; }); } } // try-with-resources 会自动关闭 executor

避坑指南与最佳实践

虽然虚拟线程非常强大,但在使用时需要注意以下几点,否则可能无法发挥其优势,甚至导致性能下降。

1. 警惕synchronized导致的“钉住”现象

这是最重要的一点!当一个虚拟线程在执行synchronized代码块或方法时发生阻塞(如 I/O 操作),它会连带着承载它的载体线程(Carrier Thread)一起被阻塞。这种现象被称为“钉住”(Pinning)。

这会严重损害性能,因为宝贵的载体线程被浪费了。

解决方案:
synchronized替换为java.util.concurrent.locks.ReentrantLockReentrantLock是在 Java 层面实现的,能与虚拟线程的挂起机制完美配合,不会导致钉住。

// 不推荐:可能导致载体线程被钉住 private final Object lock = new Object(); public void doWork() { synchronized (lock) { // 如果有 I/O 阻塞,会 pin 住载体线程 someIoOperation(); } } // 推荐:使用 ReentrantLock private final ReentrantLock lock = new ReentrantLock(); public void doWork() throws InterruptedException { lock.lock(); try { // 即使有 I/O 阻塞,虚拟线程也能正确挂起 someIoOperation(); } finally { lock.unlock(); } }

注意:如果synchronized块内只进行纯 CPU 计算,不涉及任何阻塞调用,那么它是安全的。

2. 谨慎使用ThreadLocal

ThreadLocal在每个线程中存储一份变量副本。在虚拟线程时代,你可以轻松创建百万个线程,如果大量使用ThreadLocal,可能会导致巨大的内存开销。

未来方案:
Java 正在引入ScopedValue作为ThreadLocal的替代品,它专为虚拟线程设计,内存效率更高。

3. 确保第三方库兼容

一些老旧的第三方库(尤其是数据库连接池)可能在内部使用了synchronized包裹阻塞操作。这同样会导致钉住问题。务必将常用库(如 HikariCP、Hibernate 等)升级到支持虚拟线程的最新版本。

4. 监控“钉住”情况

在生产环境中,可以通过添加 JVM 参数来监控是否有线程被钉住,以便及时发现和解决问题。

java -Djdk.tracePinnedThreads=short -jar your-app.jar
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 7:05:20

地磅专用光幕价格为何差异这么大

地磅光幕行业报价通常在800元至3500元之间&#xff0c;普通红外光幕价格为800-1800元&#xff0c;具备防尘防水功能的高端型号则在2000-3500元。影响价格的核心因素有哪些&#xff1f;第一&#xff0c;光幕的品牌与品质差异 国产品牌价格区间在800-2000元&#xff0c;进口品牌&…

作者头像 李华
网站建设 2026/4/29 7:05:13

录屏长时间录制不卡顿不黑屏:通用解决方法+5款软件实操指南

长时间录屏&#xff08;如直播回放、网课、会议&#xff09;最易出现卡顿、黑屏、闪退等问题&#xff0c;导致录制内容丢失、功亏一篑。本文聚焦“长时间录制不卡顿、不黑屏”核心需求&#xff0c;分享通用解决技巧&#xff0c;再针对5款主流录屏软件&#xff0c;给出具体执行方…

作者头像 李华
网站建设 2026/4/29 7:00:25

【GD32笔记】:P01 GD32F103C8T6 DWT的使用

kernel_dwt.c#include <stdint.h> #include "gd32f10x.h"/** *********************************************************** * brief DWT初始化配置 * param * return *********************************************************** */ void DelayInit(void)…

作者头像 李华
网站建设 2026/4/29 6:59:19

如何排查SQL存储过程死锁_分析死锁日志与索引优化

SQL Server死锁日志中deadlock-list是根节点&#xff0c;每个deadlock元素对应一次死锁事件&#xff0c;需重点分析process-list中的spid和inputbuf、resource-list中的objectname及锁模式&#xff08;如S/X&#xff09;&#xff0c;并结合执行计划排查索引与事务设计问题。怎么…

作者头像 李华
网站建设 2026/4/29 6:59:17

设备全生命周期管理:TDengine 时序数据库在制造业资产管理中的应用

摘要本文探讨 TDengine 时序数据库在制造业设备全生命周期管理中的应用&#xff0c;涵盖设备采购、运行监控、维护保养到报废处置的完整数据链条。一、设备全生命周期管理的数据需求设备管理涉及多个阶段的数据&#xff1a;采购阶段&#xff1a;设备参数、供应商信息、验收数据…

作者头像 李华