☰
Java虚拟线程(一)
2026/10/2 8:31:08 网站建设 项目流程

SpringBoot 集成虚拟线程配置 + 生产最佳实践

环境前提:SpringBoot 3.2+、JDK21+;Tomcat / Jetty / Undertow 都支持虚拟线程作为 web 工作线程。

一、SpringBoot 开启虚拟线程(2 种方式)

方式 1:全局配置(推荐,Web 容器使用虚拟线程处理 HTTP 请求)

application.yml

spring: threads: virtual: enabled: true

一行配置,SpringBoot 自动把 Tomcat 的工作线程替换为虚拟线程。

老版本 SpringBoot(3.2 之前)没有这个配置项,需要手动定制容器。

验证:写一个简单 Controller

@RestController public class TestController { @GetMapping("/test") public String test() { Thread thread = Thread.currentThread(); // 判断当前是不是虚拟线程 System.out.println("isVirtual: " + thread.isVirtual()); return "ok"; } }

访问接口,控制台输出isVirtual: true即开启成功。

方式 2:代码手动创建虚拟线程执行器(用于业务异步任务)

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Configuration public class VtConfig { // 每个任务新建一个虚拟线程,不是池 @Bean public ExecutorService virtualThreadExecutor() { return Executors.newVirtualThreadPerTaskExecutor(); } }

注入使用:

@Service public class DemoService { @Autowired private ExecutorService virtualThreadExecutor; public void doAsync() { virtualThreadExecutor.submit(() -> { // IO操作:rpc/db/http }); } }

二、容器配套参数(Tomcat 示例)

⚠️ 开启虚拟线程之后,Tomcat 的 maxThreads 不再是工作线程上限! maxThreads 此时代表载体平台线程上限,不是请求并发上限。

server: tomcat: # 载体线程池上限(默认200,一般不用改) threads: max: 200 accept-count: 100

含义:最多 200 个 OS 平台载体线程,但是可以支撑上万 HTTP 并发 IO 等待请求。

三、生产最佳实践(重点,踩坑点)

✅ 1. 必须做下游限流:Semaphore 信号量

虚拟线程创建极廉价,请求并发一旦打满,会瞬间创建上万虚拟线程,同时调用 DB/RPC,直接把下游压垮。

虚拟线程解决的是线程资源阻塞,不能替代限流。

Semaphore 示例:

// 限制最多同时20个请求访问这个RPC服务 private final Semaphore semaphore = new Semaphore(20); public void callRpc() throws InterruptedException { semaphore.acquire(); try { // http/rpc调用 } finally { semaphore.release(); } }

可以封装成注解 / 切面统一管控不同下游的并发配额。

✅ 2. 锁选择:尽量避免 synchronized

虚拟线程在synchronized块阻塞时,JVM 无法卸载载体线程,载体线程会被挂死,退化成平台线程效果。

  • 优先:ReentrantLock/ReentrantReadWriteLock
  • 禁止:长时间 IO 操作放在synchronized代码块里面

短时间、内存计算的 synchronized 没问题;IO 阻塞场景要规避。

✅ 3. ThreadLocal 使用注意

虚拟线程生命周期很短,任务结束线程直接销毁。

  • 优点:正常情况下 ThreadLocal 不用手动 remove,线程销毁自动清理
  • 风险:如果虚拟线程被缓存(自己池化 VT,强烈不建议),会发生内存泄漏;
  • 建议:业务代码依旧养成try-finally清理 ThreadLocal 习惯,兼容双环境切换。

✅ 4. 不要自己池化虚拟线程

❌ 错误写法:

// 毫无意义,浪费虚拟线程能力 ExecutorService pool = Executors.newFixedThreadPool(10, Thread.ofVirtual().factory());

newFixedThreadPool 会复用虚拟线程,失去虚拟线程随用随销毁的特性,完全没必要。

✅ 正确:newVirtualThreadPerTaskExecutor(),一个任务一个虚拟线程。

✅ 5. CPU 密集任务剥离出来

Web 接口里如果有大量 CPU 计算(序列化、加密、大数据运算),不要丢虚拟线程,单独使用固定大小平台线程池(核心数)执行,防止载体线程被 CPU 任务占满,影响其他 IO 请求。

✅ 6. 监控指标

需要监控这几项,生产必备:

  1. 载体线程数量(Tomcat threads.max)
  2. Semaphore 等待队列长度
  3. DB 连接池活跃连接数(虚拟线程并发高,DB 连接池很容易成为瓶颈!)

重点:虚拟线程并发高了之后,数据库连接池才是瓶颈,不是线程。连接池配置要配套调整。

✅ 7. 异常处理

虚拟线程默认未捕获异常会直接打印日志,不会向外抛出。 建议创建时自定义 UncaughtExceptionHandler:

Thread.Builder builder = Thread.ofVirtual().uncaughtExceptionHandler((t, e) -> { log.error("虚拟线程任务异常", e); });

四、适合 / 不适合启用虚拟线程的 SpringBoot 场景

✅ 推荐开启:

  • 后端接口,大量等待:DB 查询、HTTP 调用、MQ 等待、IO 阻塞
  • 微服务网关、BFF 层,大量外部 RPC 调用

❌ 不推荐开启:

  • 大量 CPU 计算型接口
  • 依赖很多 native 代码 / JNI(部分 native 阻塞不支持虚拟线程卸载)
  • JDK 版本低于 21,SpringBoot 低于 3.2

五、迁移建议(老项目升级)

  1. 先测试环境开启,压测,重点观察:DB 连接池、下游 RPC 负载
  2. 优先 Web 容器虚拟线程,业务异步任务按需引入,不要一次性全量改造
  3. 给所有下游 DB、第三方 RPC 增加 Semaphore 并发保护
  4. 检查代码,把长时间 IO 的 synchronized 块替换成显式锁

六、总结(配套前面内容)

SpringBoot3.2 + 可以一行配置开启虚拟线程,让 Tomcat 使用虚拟线程处理 HTTP 请求;虚拟线程不是线程池,任务随用随建;优势是 IO 阻塞时释放底层载体平台线程,大幅提升 IO 场景并发能力;但必须搭配信号量做下游限流,避免压垮数据库和 RPC,同时尽量避免synchronized内长时间阻塞。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询