java新版本-java25学习笔记(三)虚拟线程要和资源边界一起看

发表于 2026-02-10 21:52 1936 字 10 min read

Spring AI学习笔记(四)工具调用和MCPSpring AI学习笔记(三)RAG从文档入库到回答Spring AI学习笔记(二)ChatClient从怎么调到怎么封装Spring AI学习笔记(一)它到底解决什么问题java新版本-java25学习笔记(四)用JFR和GC日志做一次体检java新版本-java25学习笔记(三)虚拟线程要和资源边界一起看java新版本-java25学习笔记(二)运行时基线先统一java新版本-java25学习笔记(一)LTS版本对比和学习路线主流AI Agent能力对比与工程选型我用Kiro做了个自己的工具站盘一盘虚拟线程我用Trae做了个AstrBot的AI角色扮演插件Python初学笔记(六)常用标准库先学这几个Python初学笔记(五)读写文件和处理异常Python初学笔记(四)函数让代码开始有结构Python初学笔记(三)条件、循环和推导式Python初学笔记(二)变量和基础类型比想象中重要Python初学笔记(一)先把环境和运行方式弄明白主流AI大模型能力对比Java 21和Spring Boot 3升级笔记(五)日志指标与可观测性Java 21和Spring Boot 3升级笔记(四)数据访问层适配Java 21和Spring Boot 3升级笔记(三)虚拟线程使用边界Java 21和Spring Boot 3升级笔记(二)Jakarta迁移要点Java 21和Spring Boot 3升级笔记(一)工程基线整理魔法值怎么能忍!JPA的Specification大改造!处理生僻字乱码:JPA框架对于Oracle的NVarchar2,NChar,NClob类型支持Redis Stream能不能当轻量消息队列用RocketMQ 5学习笔记:普通消息之外要看什么事件流不是换个消息队列这么简单Kubernetes学习笔记04:发布、排障和观测Kubernetes学习笔记03:配置、密钥和存储Kubernetes学习笔记02:Deployment、Service和IngressKubernetes学习笔记01:Pod和控制器mysql索引原理02--存储引擎索引的实现mysql索引原理01--索引的数据结构
  这篇继续记录,主题是虚拟线程。   Java 25 评估时,虚拟线程仍然是最容易被关注的能力之一。   它在 Java 21 已经正式可用,现在很多框架和组件的适配也更成熟。对后端项目来说,虚拟线程最吸引人的地方是:可以继续写同步代码,同时降低阻塞等待的线程成本。...

前言

  这篇继续记录java新版本-java25学习笔记,主题是虚拟线程。

  Java 25 评估时,虚拟线程仍然是最容易被关注的能力之一。

  它在 Java 21 已经正式可用,现在很多框架和组件的适配也更成熟。对后端项目来说,虚拟线程最吸引人的地方是:可以继续写同步代码,同时降低阻塞等待的线程成本。

  但我越来越觉得,虚拟线程落地时最重要的不是“能创建多少线程”,而是“资源边界在哪里”。

从 Java 21 到 Java 25 怎么看虚拟线程

  虚拟线程不是 Java 25 才有。它在 Java 21 已经正式可用,所以如果只从 LTS 版本看,可以这样理解。

版本和虚拟线程相关的理解
Java 8 / 11 / 17主要还是平台线程和线程池模型,阻塞 IO 的线程成本比较高
Java 21虚拟线程正式可用,可以开始用同步写法承载更多阻塞等待
Java 25继续在更成熟的生态里验证虚拟线程,并结合 Scoped Values、Structured Concurrency 等能力学习新的并发模型

  所以 Java 25 下学习虚拟线程,不是为了追一个新名词,而是为了补上现代 Java 并发模型。

线程便宜了,不代表资源无限

  传统平台线程比较贵,所以线程池大小本身就限制了并发。

虚拟线程便宜很多,于是更多请求可以同时处在等待状态。这对 IO 密集型服务是好事,但也可能把压力推给数据库、Redis、下游接口和消息队列。

比如以前 Tomcat 线程池 200,最多也就 200 个请求同时阻塞在数据库前。启用虚拟线程后,如果没有其他限制,等待数据库连接的请求可能更多。

所以虚拟线程落地时,要把限流点从线程池转移到真实资源上:

1.数据库连接池。 2.HTTP Client 连接池。 3.Redis 连接池。 4.下游服务限流。 5.业务队列长度。 6.关键锁。

线程不是瓶颈了,瓶颈会更快暴露在别处。

先选 IO 密集接口

  不是所有接口都适合第一批试虚拟线程。

我会优先选 IO 密集、同步阻塞、业务边界清楚的接口。比如查询聚合接口、需要调用多个下游的接口、等待数据库比较多的接口。

不适合先选 CPU 密集接口。比如复杂报表计算、批量加密、图片处理、规则引擎大计算。这些任务主要消耗 CPU,虚拟线程不会让 CPU 算得更快。

试点时要有对照组。同样流量、同样数据、同样下游环境下,对比平台线程和虚拟线程的吞吐、延迟、CPU、内存、连接池等待。

没有对照,就很难判断收益是不是虚拟线程带来的。

一个 Java 25 虚拟线程小练习

  先用一个最小例子感受虚拟线程适合做什么。

新建virtual-thread-demo.java

import java.util.concurrent.Executors;
import java.util.stream.IntStream;

void main() throws Exception {
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        var tasks = IntStream.rangeClosed(1, 5)
            .mapToObj(i -> executor.submit(() -> {
                Thread.sleep(300);
                return "task-" + i + " run by " + Thread.currentThread();
            }))
            .toList();

        for (var task : tasks) {
            System.out.println(task.get());
        }
    }
}

运行:

java virtual-thread-demo.java

  这个例子里的任务主要是在等待,所以虚拟线程很合适。如果把里面换成大量 CPU 计算,虚拟线程不会让 CPU 变多。

数据库连接池是第一道墙

  虚拟线程压测时,我最先看数据库连接池。

指标包括:

1.active 连接数。 2.idle 连接数。 3.pending 等待数。 4.获取连接耗时。 5.SQL 耗时。 6.数据库 CPU。

如果启用虚拟线程后,接口吞吐没上去,连接池等待明显增加,就说明瓶颈在数据库或连接池。

这时候盲目调大连接池不一定对。连接池变大,数据库压力也会变大。要看数据库本身能不能承受,SQL 是否需要优化,是否应该做缓存或限流。

虚拟线程只是把问题暴露得更明显,不会替你优化 SQL。

下游调用要有超时

  虚拟线程让等待成本降低,但等待时间仍然影响用户体验。

如果下游 HTTP 调用没有超时,虚拟线程也会一直等。等待线程不贵,但请求上下文、内存、连接、业务资源仍然占着。

所以所有下游调用都应该有明确超时:

1.连接超时。 2.读取超时。 3.整体请求超时。 4.重试次数。 5.熔断或限流策略。

虚拟线程不应该成为放松超时治理的理由。

锁竞争要用 JFR 看

  虚拟线程数量多以后,锁竞争问题会更容易出现。

某个原本不起眼的synchronized方法,在更高并发下可能成为热点。缓存刷新、编号生成、单例工具、全局 Map 更新,都可能造成等待。

我比较喜欢用 JFR 看这类问题。它能记录锁、阻塞、线程状态和热点方法。比只看日志更直接。

如果发现大量虚拟线程卡在同一把锁上,就要回到代码里看锁粒度是否过大,是否可以用并发集合、分段锁、无锁结构,或者把共享状态拆掉。

ThreadLocal 要收敛

  很多 Java Web 项目里 ThreadLocal 用得很随意。

用户上下文、租户 ID、traceId、数据权限、临时缓存,都可能放进去。虚拟线程下 ThreadLocal 不是不能用,但要更克制。

首先,请求结束必须清理。其次,不要放大对象。再次,不要依赖线程复用语义。

虚拟线程通常是任务级别的,用完就结束。那些基于线程池复用假设写出来的 ThreadLocal 优化,可能不再合适。

如果只是上下文传递,应该优先走框架支持的上下文机制,或者在合适的 JDK 版本里评估 Scoped Value。

观测口径要调整

  启用虚拟线程后,传统线程数指标会变得没那么直观。

你可能看到很多虚拟线程,但这不等于系统异常。要看平台线程、CPU、内存、连接池、锁等待和请求延迟。

日志里也不要把线程名当成业务含义。虚拟线程名称可能很多,排查要依赖 traceId、requestId、业务单号,而不是线程名。

指标看板也要增加资源边界指标。否则只看 JVM 线程数,很容易误判。

什么时候不该用

  虚拟线程不是必须全开。

如果一个服务主要是 CPU 计算,或者依赖的库在虚拟线程下表现不好,或者团队还没有足够观测手段,就可以先不启用。

如果启用后只是把数据库打满、下游打挂,也不是成功。

技术落地要看系统整体收益。虚拟线程让并发模型更轻,但它不负责业务容量规划。

小结

  Java 25 下继续评估虚拟线程,是很值得做的事情。

  但落地重点不是追求线程数量,而是把资源边界看清楚。数据库连接池、HTTP 超时、下游限流、锁竞争、ThreadLocal、观测指标都要一起调整。

  虚拟线程让同步 IO 代码更有伸缩性,但系统稳定性仍然来自明确的边界和足够的证据。

喜欢的话,留下你的评论吧~

© 2019 - 2026 VincentHo @VincentHo
Powered by theme astro-koharu · Inspired by Shoka