You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 3.2+虚拟线程并行调用外部服务的正确实现方式

关于Spring Boot 3.2中RestClient结合虚拟线程并行调用外部服务的问题

1. 顺序调用代码的问题

你给出的第一种写法是同步串行执行,完全无法实现并行调用。代码会先等待performGetRequest(uri1)返回结果后,才会执行performGetRequest(uri2),两个请求依次执行,完全没用到虚拟线程的并行能力,不符合你并行调用的需求。

2. Future写法的核心注意点

第二种写法要实现并行,关键看performGetRequest的实现逻辑:

  • 如果这个方法只是单纯同步调用RestClient(比如return restClient.get().uri(uri).retrieve().body(String.class);),那直接返回Future毫无意义——你必须把每个请求任务提交到虚拟线程池,让它们在独立的虚拟线程上运行,才能真正并行。正确的写法示例:
ExecutorService virtualThreadExecutor = Executors.newVirtualThreadPerTaskExecutor();
Future<String> response1 = virtualThreadExecutor.submit(() -> performGetRequest(uri1));
Future<String> response2 = virtualThreadExecutor.submit(() -> performGetRequest(uri2));

String result1 = response1.get();
String result2 = response2.get();
virtualThreadExecutor.shutdown();
  • 要是你没把任务提交到虚拟线程池,直接返回Future本质还是在当前线程顺序执行两个请求,达不到并行效果。

3. 是否需要手动创建虚拟线程池?

虽然你配置了spring.threads.virtual.enabled=true,但这个配置仅让Spring的默认TaskExecutor(比如@Async注解使用的线程池)改用虚拟线程,不会自动帮你并行执行代码里的同步调用。

所以要实现并行调用,你需要手动创建虚拟线程池(比如Executors.newVirtualThreadPerTaskExecutor()),或者注入Spring提供的TaskExecutor(启用虚拟线程后,它已经是虚拟线程池)。

另外,用try-with-resources包裹ExecutorService是更安全的做法,它会自动关闭线程池,无需手动调用shutdown():

try (ExecutorService service = Executors.newVirtualThreadPerTaskExecutor()) {
    Future<String> response1 = service.submit(() -> performGetRequest(uri1));
    Future<String> response2 = service.submit(() -> performGetRequest(uri2));
    return response1.get() + response2.get();
}

补充:Spring 6.1+的简化异步写法

Spring Boot 3.2基于的Spring 6.1提供了更简洁的异步调用方式,结合虚拟线程可以这样写:

CompletableFuture<String> response1 = CompletableFuture.supplyAsync(() -> 
    restClient.get().uri(uri1).retrieve().body(String.class), 
    Executors.newVirtualThreadPerTaskExecutor()
);
CompletableFuture<String> response2 = CompletableFuture.supplyAsync(() -> 
    restClient.get().uri(uri2).retrieve().body(String.class), 
    Executors.newVirtualThreadPerTaskExecutor()
);

return CompletableFuture.allOf(response1, response2)
    .thenApply(v -> response1.join() + response2.join())
    .join();

也可以直接注入Spring的TaskExecutor替代手动创建的线程池,更贴合Spring生态。

内容的提问来源于stack exchange,提问作者William N

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 21:19:59