SpringBoot 3.4启用虚拟线程却串行执行的问题排查
问题原因及解决方案
为什么会出现串行执行、线程名是Tomcat线程的情况?
虚拟线程不会自动“接管”同步代码
你配置的spring.threads.virtual.enabled=true只是让Spring内置的任务执行器默认使用虚拟线程,但HTTP请求的入口仍然由Tomcat的平台线程(tomcat-handler-xxx)处理。你代码里是同步调用generateExpression(),这些代码会直接在Tomcat线程上串行执行,不会自动切换到虚拟线程。同步RestClient调用阻塞了当前线程
generateExpression()里的RestClient调用是同步阻塞式的,会一直占用Tomcat线程直到请求返回,完全没有触发虚拟线程的调度机制。Tomcat默认未启用虚拟线程处理请求
要让Tomcat用虚拟线程处理HTTP请求,需要额外添加配置:server.tomcat.threads.virtual.enabled=true但就算开启了这个配置,同步调用的代码依然会串行执行,只是请求处理线程换成虚拟线程而已。
怎么实现并行结构化并发?
正如你提到的,必须用StructuredTaskScope来主动创建虚拟线程并行执行任务,修正后的代码示例:
public String testFlow() { final Instant start = Instant.now(); // 用StructuredTaskScope管理虚拟线程任务,ShutdownOnFailure表示任一任务失败就终止所有 try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { // 并行提交三个生成表达式的任务 Supplier<String> expressionTask = scope.fork(this::generateExpression); Supplier<String> dummy1Task = scope.fork(this::generateExpression); Supplier<String> dummy2Task = scope.fork(this::generateExpression); // 等待所有任务完成,若有失败直接抛出异常 scope.join(); scope.throwIfFailed(); // 获取三个任务的结果 String expression = expressionTask.get(); String dummy1 = dummy1Task.get(); String dummy2 = dummy2Task.get(); log.info("Generated expression: {}", expression); // 后续业务逻辑... } catch (InterruptedException | ExecutionException e) { throw new RuntimeException("并行任务执行失败", e); } // 计算耗时等逻辑... }
额外说明
StructuredTaskScope是Java21原生的结构化并发API,能保证子任务和父任务生命周期绑定,避免线程泄露,是实现虚拟线程并行的推荐方式。- 若不想用原生API,也可以使用Spring的
@Async注解,Spring Boot3.4开启虚拟线程开关后,@Async默认会使用虚拟线程池,但结构化并发的可靠性和可读性更强。
内容的提问来源于stack exchange,提问作者Somjit
相关产品推荐
相关产品推荐

