前言
虚拟线程从 Java 21 正式可用以后,现在已经不算新鲜概念了。
很多文章会强调它能创建大量线程,能让同步阻塞代码也有不错的并发能力。这些都对,但我觉得落地时不能只盯着“能开多少线程”。
虚拟线程改变的是 Java 处理并发 IO 的成本模型,但它没有取消数据库连接池、HTTP 下游容量、锁竞争、CPU 瓶颈这些现实限制。
它适合什么
虚拟线程适合 IO 密集型任务。
比如一个接口里要查数据库、调下游 HTTP、读 Redis、写日志,大部分时间都在等待。这种场景下,传统平台线程被阻塞时成本比较高。虚拟线程阻塞时可以让出载体线程,系统能承载更多等待中的任务。
它尤其适合同步编程模型。以前为了高并发,可能要写异步回调或响应式代码,复杂度会上来。虚拟线程让我们可以继续写比较直观的同步代码。
但如果任务是 CPU 密集型,比如大规模加密、图片处理、复杂计算,虚拟线程帮不上太多。CPU 就那么多核,算不过来就是算不过来。
不要把连接池打爆
虚拟线程让请求线程变便宜,但数据库连接没有变便宜。
如果一个接口每次都要查数据库,虚拟线程并发上来以后,数据库连接池可能先被打满。请求都在等连接,看起来线程很多,实际吞吐不一定提升。
所以启用虚拟线程后,更要关注连接池:
1.最大连接数。 2.等待连接耗时。 3.活跃连接数。 4.慢 SQL。 5.数据库 CPU 和锁等待。
不要以为线程不贵了,下游资源也无限了。虚拟线程只是把应用层等待成本降低,下游容量仍然是硬约束。
synchronized 和锁要检查
虚拟线程最怕一些不经意的锁竞争。
如果大量虚拟线程在某个synchronized块上排队,吞吐照样会掉。更麻烦的是某些阻塞操作在特定情况下可能让载体线程被占住,影响调度效果。
日常业务代码里,很多锁是历史遗留的。比如缓存刷新、单例工具、编号生成、定时任务保护。这些平时没问题,并发上来后就可能变成瓶颈。
所以测试虚拟线程时,不只要看接口吞吐,还要看线程状态、锁竞争和 JFR 事件。
ThreadLocal 要重新审视
Java Web 项目里 ThreadLocal 很常见。
用户上下文、租户上下文、traceId、数据源路由、登录信息,都可能放 ThreadLocal。虚拟线程下 ThreadLocal 仍然可用,但线程数量可能更多,使用方式要谨慎。
首先要保证请求结束后清理。否则上下文泄漏会更难排查。
其次,不要往 ThreadLocal 放大对象。虚拟线程数量多时,大对象会放大内存压力。
如果只是传递只读上下文,可以关注 Scoped Value 这类更适合结构化上下文传递的能力。不过在业务项目里,是否使用还要看 JDK 版本和团队熟悉程度。
线程池思维要调整
传统平台线程里,我们经常用线程池控制并发。线程池大小本身就是一种限流。
虚拟线程不太适合用固定小线程池来复用。它的使用方式更像是每个任务一个虚拟线程。
但并发仍然要控制,只是控制点应该转移到资源边界上。比如数据库连接池、HTTP Client 连接池、信号量、限流器、队列长度。
如果某个下游最多承受 100 并发,就用限流或信号量控制,不要指望虚拟线程自己知道。
Spring 项目怎么试
Spring Boot 3.x 对虚拟线程已经有比较方便的支持。
但我不建议全项目一键打开后直接上线。可以先选几个 IO 密集接口做压测对比。
观察指标包括:
1.接口吞吐。 2.P95 和 P99 延迟。 3.CPU 使用。 4.数据库连接等待。 5.GC 和内存。 6.下游错误率。 7.锁竞争。
如果只是吞吐提高但下游错误率也提高,那不是成功。说明并发压力被推给下游了。
日志和排查会有变化
虚拟线程数量多以后,线程 dump 会比以前更热闹。
排查时不能简单看到很多线程就紧张。要看它们在等什么,是等数据库连接、等 HTTP、等锁,还是在执行 CPU 任务。
JFR 对虚拟线程排查很有帮助。它能看到线程调度、阻塞、锁和方法采样。相比直接看巨大线程栈,JFR 更容易找到重点。
日志里的线程名也可能变化。如果团队有依赖线程名判断业务的习惯,最好调整掉。线程名不是稳定业务语义。
批处理任务也要谨慎
虚拟线程不只会出现在 Web 请求里,也可能被用到批处理任务。
比如一次批量同步十万条数据,如果每条数据都开一个虚拟线程去调下游,看起来代码很简单,但下游服务和数据库很可能扛不住。
批处理更需要控制节奏。可以把任务拆批,每批固定大小,再用信号量限制同时执行的数量。虚拟线程负责降低等待成本,批处理框架负责控制总体压力。
我不太建议把“每个任务一个虚拟线程”理解成“所有任务一起发出去”。工程里永远要问一句:下游能不能接住。
小结
虚拟线程是 Java 并发模型里很重要的一步,尤其适合同步 IO 密集型后端服务。
但它不是无限并发按钮。落地时要看数据库连接池、下游容量、锁竞争、ThreadLocal、内存和观测工具。
我的建议是先用小范围压测验证,再按资源边界做限流。虚拟线程让线程便宜了,但系统里真正贵的东西仍然要认真管理。
喜欢的话,留下你的评论吧~