如何使用Java8 CompletableFuture实现列表forEach异步调用Rest接口
实现注意事项
- 原代码在循环外创建单个
Student对象,并行场景下存在多线程属性覆盖的线程安全问题,必须改为每次循环内部创建独立Student实例 - 不要在异步任务中直接操作非线程安全的
ArrayList,改为先收集异步任务,全部完成后统一提取结果,避免并发写入异常 - 建议自定义IO密集型线程池,避免使用CompletableFuture默认的公共ForkJoinPool,防止和其他业务异步任务争抢资源
完整改造代码
1. 自定义线程池(推荐配置)
import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.util.concurrent.*; // 线程池参数可根据实际压测结果调整,IO密集型场景核心线程数建议设置为2*CPU核心数 private static final ExecutorService REST_CALL_POOL = new ThreadPoolExecutor( 10, 20, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), new ThreadFactoryBuilder().setNameFormat("rest-request-thread-%d").build() );
2. 改造后的业务方法
private static List<List<Object>> getObjList(List<List<String>> combine) { // 1. 遍历集合创建所有异步请求任务 List<CompletableFuture<List<Object>>> futureList = IntStream.range(0, combine.size()) .mapToObj(index -> CompletableFuture.supplyAsync(() -> { // 每个任务内部创建独立Student对象,避免线程安全问题 Student std = new Student(); std.setList(combine.get(index)); std.setPageId(index + 1); // 替换为实际的Rest调用逻辑即可 List<Object> response = restTemplate.postForObject("你的请求接口地址", std, List.class); return response; }, REST_CALL_POOL) // 可选:单个请求异常捕获,避免单条失败导致整个任务报错 .exceptionally(e -> { log.error("第{}个Rest请求调用失败", index + 1, e); // 异常场景返回默认值,可根据业务需求调整 return Collections.emptyList(); })) .collect(Collectors.toList()); // 2. 等待所有异步任务全部执行完成 CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).join(); // 3. 按原始集合顺序提取所有请求结果返回 return futureList.stream() .map(CompletableFuture::join) .collect(Collectors.toList()); }
3. 主调用逻辑调整
List<String> list1 = new ArrayList<>(); list1.add("Class1"); list1.add("Class2"); List<String> list2 = new ArrayList<>(); list2.add("Book1"); list2.add("Book2"); List<List<String>> combine = new ArrayList<>(); combine.add(list1); combine.add(list2); // 直接调用方法获取结果,不需要提前传入空集合 List<List<Object>> finalResponse = getObjList(combine);
关键逻辑说明
- 用
IntStream.range生成下标替代原来的AtomicInteger计数,避免多线程计数的额外开销 allOf方法会等待所有异步任务执行完成后再统一收集结果,返回顺序和原始combine集合的顺序完全一致- 线程池使用完成后如果是单次运行的程序可以主动调用
REST_CALL_POOL.shutdown()关闭资源,常驻服务不需要关闭
内容的提问来源于stack exchange,提问作者LearnConcept
相关产品推荐
相关产品推荐

