本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog2,之前的微信号好友位已满,备注:返现
受密码保护的文章请关注“业余草”公众号,回复关键字“0”获得密码
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
【腾讯云】1核2G5M轻量应用服务器50元首年,高性价比,助您轻松上云
Java 虚拟线程已凉?凉透了?
回想起虚拟线程刚推出时的那个热乎劲,历历在目。然而,热的快凉的也快。Java 虚拟线程从“百万并发梦”到“无人问津”,不到三年而已。
最近,我在 Github 上看了几个开源项目,曾经宣传的 Spring Boot 一行配置开启百万并发并没有出现,多数生产环境敢开虚拟线程的项目,十个里找不出一个。这到底是为何呢?今天我们掰开揉碎了聊一聊。
虚拟线程的范式革命
2023 年 9 月,Java 21 正式发布,虚拟线程(Virtual Threads)从预览版转正。Oracle 的 PPT 是这么画的:
- 创建成本
趋近于零(几百字节 vs 平台线程 1MB+) - 单机轻松支撑
百万级并发 - 完全兼容现有
ThreadAPI,零侵入迁移 - 同步代码写出异步性能,
告别 Reactive 编程的回调地狱
Spring Boot 3.2 更是直接甩出一个配置:
spring:
threads:
virtual:
enabled: true
一行 YAML,Tomcat 线程池秒变虚拟线程。当时的技术圈,仿佛已经看到了 Java 拳打 Go、脚踢 Node.js 的美好未来。
然而现实是到了 2026 年,你去问十个 Java 后端,九个没在生产环境开过虚拟线程。剩下那个开了的,大概率正在排查一个诡异的线程阻塞问题。
曾经说好的“范式革命”,怎么就变成了“圈地自嗨”?
虚拟线程的四宗罪
虚拟线程画的饼,看起来“破裂”了。总结起来,大概有 4 宗罪,下面我们一一展开来看。
第一宗罪
排在第一的应该是 Pinning,一个看不见的锁,要命的坑。
虚拟线程的核心假设是遇到 I/O 阻塞时,JVM 把虚拟线程从载体线程(Carrier Thread)上摘下来,让载体线程去干别的。
但如果你在 synchronized 块里搞 I/O,JVM 摘不下来,虚拟线程被“钉”(Pin)在了载体线程上。
这意味着什么?你的“百万虚拟线程”背后,其实只有寥寥几个载体线程在干活,I/O 阻塞时它们全被占满了,新的虚拟线程调度不上去。性能不升反降,还可能直接卡死。
更恶心的是,这个问题极其隐蔽。你的代码里可能只有一行 synchronized,或者某个第三方库(比如老版本 JDBC 驱动)内部用了 synchronized,就能让整个虚拟线程池变成“假并发”。
Oracle 官方给的检测方案是开 JVM 参数:
-Djdk.tracePinnedThreads=full
但生产环境你敢随便加诊断参数吗?而且就算检测到了,改 synchronized 为 ReentrantLock 这件事,在 legacy 代码库里简直是“史诗级重构”。
- 好消息是:Java 24 的 JEP 491 已经解决了大部分 Pinning 问题,
synchronized不再钉死载体线程。 - 坏消息是:2026 年的生产环境,主流还是 Java 21 LTS。升级 Java 25?你先说服运维老大。更别说 Java 8 了。
第二宗罪
排在第二的是 ThreadLocal,它从“线程保险箱”变成“内存炸弹”。
Java 生态里,ThreadLocal 是个让人又爱又恨的东西。SLF4J 的 MDC、Spring 的事务上下文、各种框架的上下文传递,全指望它。
但虚拟线程是海量创建、随时销毁的。每个虚拟线程一个 ThreadLocal,百万并发就是百万份 ThreadLocal 副本。
更要命的是,很多框架的 ThreadLocal 里塞的是重量级对象,数据库连接、缓存会话、安全上下文。虚拟线程一多,GC 压力直接爆炸。
Java 21 推出了 ScopedValue 作为 ThreadLocal 的替代方案, 但生态迁移?又是一个“从 JDK 8 升级到 17”级别的浩大工程。
第三宗罪
排在第三的是框架生态。支持了,但又没完全支持。
表面上看,Spring Boot 3.2+、Quarkus、Micronaut 都“支持”虚拟线程。
但“支持”和“能用”、“好用”是三回事。
- 数据库连接池:HikariCP 没问题,但某些 JDBC 驱动内部还有
synchronized - Redis 客户端:Lettuce 可以,Jedis 老版本不行
- HTTP 客户端:OkHttp、Apache HttpClient 需要升级版本
- 日志框架:Logback、Log4j2 对虚拟线程的适配版本要求严格
- 监控链路:Prometheus 的 JVM 指标、SkyWalking 的链路追踪,虚拟线程的堆栈和线程名都是一团糟
你开一个虚拟线程,发现问题后去查日志,发现线程名全是 ""(空字符串)或者一串无意义的数字,排查问题直接梦回 Java 8。哎,说多了都是泪。
第四宗罪
最后是在 CPU 密集型场景,不是给你的,别硬上。
很多团队对虚拟线程有个误解,用了虚拟线程,性能就能飞升。
这个认识是错的。
虚拟线程解决的是I/O 阻塞导致的线程资源浪费,不是 CPU 算力不足。
如果你的服务是:
- 图片压缩/视频转码
- 大数据 ETL
- 复杂规则引擎计算
- 密集加密解密
虚拟线程不仅帮不上忙,还可能因为调度开销让性能更差。这就导致一个尴尬的局面,最需要高并发的 I/O 密集型服务(网关、聚合层),往往也是技术债最重、最不敢动的服务。
虚拟线程真的凉了吗?
文章配图参见我的公众号:https://mp.weixin.qq.com/s/cRILNAYrmm1rnSVFEacd3A。
先别急着唱衰。虽然“全民虚拟线程”的盛况没有出现,但真正适合的场景里,虚拟线程已经开始悄悄发力了。
来自老外的 2026 年的生产实测数据表示,在典型的 I/O 密集型场景,比如 HTTP 网关、数据库聚合服务、消息消费端等,虚拟线程的表现确实亮眼。
| 指标 | 传统线程池 | 虚拟线程 | 提升 |
|---|---|---|---|
| 并发承载 | ~2000 线程上限 | 20万+ 虚拟线程 | 100 倍 |
| 内存占用 | 2GB+(线程栈) | <200MB | 10 倍 |
| 吞吐量(I/O 场景) | 800 QPS | 2500+ QPS | 3 倍+ |
| 平均延迟 | 280ms | 90ms | 降低 60% |
包括 Spring Boot 3.2+ 的适配也确实做到了“一键开启”的便捷度。
但问题不在于技术本身不行,而在于“迁移成本 > 收益”的算账逻辑。
一个运行了 5 年的 Spring Boot 2.x 项目,升级到 3.x 本身就需要。
- JDK 8 → 21 的兼容性改造(Jakarta EE 命名空间、移除的 API)
- Spring Boot 2.7 → 3.x 的配置变更
- 排查所有
synchronized和 ThreadLocal 的使用 - 升级所有依赖库到虚拟线程友好版本
- 全链路压测和灰度验证
这一套下来,三个月人力没了。而业务方的需求是下周上线新功能。虽然现在有了 AI,能加快速度,但是这件事对程序员来说终究是出力不讨好的,出了问题了还可能要背锅。做好了也不一定涨薪,这也是 Java 8 根深蒂固的原因之一吧。
虚拟线程终究不是“银弹”,但也不是“鸡肋”。它的定位更像是Java 生态的渐进式改良,而非“颠覆式革命”。
虚拟线程没凉,只是从褪去了网红的底色,回归了现实。它不再是那个被吹上天的“百万并发银弹”,而是逐渐沉淀为 Java 并发工具箱里的一个“常规选项”。
当初 Java 圈宣传得太猛了,“一行代码百万并发”的口号喊得太响,导致大家以为它能解决一切。结果发现它只是一个更好的线程模型,不是魔法。
最后
如果你还在用 JDK 8,虚拟线程确实跟你没关系。哈哈,Java 17 也没关系。
但如果你已经在用 Java 21+,千万不要在所有服务或场景里无脑开启虚拟线程。
虚拟线程不是凉了,而是回归了正常。

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号:xttblog2。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!
本文原文出处:业余草: » Java 虚拟线程已凉