SpringBoot 集成虚拟线程配置 生产最佳实践环境前提SpringBoot 3.2、JDK21Tomcat / Jetty / Undertow 都支持虚拟线程作为 web 工作线程。一、SpringBoot 开启虚拟线程2 种方式方式 1全局配置推荐Web 容器使用虚拟线程处理 HTTP 请求application.ymlspring: threads: virtual: enabled: true一行配置SpringBoot 自动把 Tomcat 的工作线程替换为虚拟线程。老版本 SpringBoot3.2 之前没有这个配置项需要手动定制容器。验证写一个简单 ControllerRestController 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. 监控指标需要监控这几项生产必备载体线程数量Tomcat threads.maxSemaphore 等待队列长度DB 连接池活跃连接数虚拟线程并发高DB 连接池很容易成为瓶颈重点虚拟线程并发高了之后数据库连接池才是瓶颈不是线程。连接池配置要配套调整。✅ 7. 异常处理虚拟线程默认未捕获异常会直接打印日志不会向外抛出。 建议创建时自定义 UncaughtExceptionHandlerThread.Builder builder Thread.ofVirtual().uncaughtExceptionHandler((t, e) - { log.error(虚拟线程任务异常, e); });四、适合 / 不适合启用虚拟线程的 SpringBoot 场景✅ 推荐开启后端接口大量等待DB 查询、HTTP 调用、MQ 等待、IO 阻塞微服务网关、BFF 层大量外部 RPC 调用❌ 不推荐开启大量 CPU 计算型接口依赖很多 native 代码 / JNI部分 native 阻塞不支持虚拟线程卸载JDK 版本低于 21SpringBoot 低于 3.2五、迁移建议老项目升级先测试环境开启压测重点观察DB 连接池、下游 RPC 负载优先 Web 容器虚拟线程业务异步任务按需引入不要一次性全量改造给所有下游 DB、第三方 RPC 增加 Semaphore 并发保护检查代码把长时间 IO 的 synchronized 块替换成显式锁六、总结配套前面内容SpringBoot3.2 可以一行配置开启虚拟线程让 Tomcat 使用虚拟线程处理 HTTP 请求虚拟线程不是线程池任务随用随建优势是 IO 阻塞时释放底层载体平台线程大幅提升 IO 场景并发能力但必须搭配信号量做下游限流避免压垮数据库和 RPC同时尽量避免synchronized内长时间阻塞。