CompletableFuture并行化可将首页5个200ms串行接口(总1s)压缩至约200ms,通过supplyAsync异步发起、allOf等待、thenCompose处理依赖、exceptionally兜底异常,并须使用自定义线程池避免阻塞公共ForkJoinPool。

用 CompletableFuture 把首页多个串行接口改成并行调用,能显著缩短总耗时——原来 5 个接口各 200ms、串行要 1s,现在并行基本就是 200ms 左右。
把串行调用改成并行组合
原来首页可能这样写:
user = getUser();order = getOrder(user.id);
cart = getCart(user.id);
notice = getNotice(user.id);
banner = getBanner();
每个方法都阻塞等待上一个返回。换成 CompletableFuture 后,可让它们“同时发起”:
- 用
supplyAsync()包装每个接口调用(注意线程池别用默认的,避免影响其他业务) - 用
allOf()等待全部完成,再用join()拿结果 - 结果对象提前定义好,用
thenApply()或手动组装
处理依赖关系:有的接口要等前一个结果
比如「订单列表」需要用户 ID,但「用户信息」和「广告位」互不依赖。这时不能全扔进 allOf,得拆开编排:
立即学习“Java免费学习笔记(深入)”;
- 先并行拉取
user和banner - 用
user.thenCompose(u -> supplyAsync(() -> getOrder(u.id)))表达“拿到 user 后再查 order” - 用
thenCombine()合并两个独立异步结果(如 user + cart → 封装成 HomePageDTO)
异常统一兜底,别让一个失败拖垮整个首页
CompletableFuture 默认遇到异常就中断链式调用,首页不能因为“消息未读数”接口超时就白屏:
- 每个
supplyAsync后接exceptionally(),返回兜底值(如空列表、默认 banner) - 避免在
thenAccept()里写业务逻辑,容易漏异常;优先用handle(),它同时接收正常结果和异常 - 日志记录异常即可,不要 throw 出去,防止
join()阻塞报错
线程池必须自定义,别碰 ForkJoinPool.commonPool()
默认线程池是 JVM 共享的,IO 型任务(如 HTTP 调用)容易把它占满,导致定时任务、Stream 并行流等卡住:
- 创建专用线程池:
new ThreadPoolExecutor(5, 20, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue(100)) - 所有
supplyAsync都显式传入该池子:supplyAsync(() -> api.getUser(), httpClientPool) - 池大小按首页并发量预估,一般 5~10 核心线程够用,加队列防突增


















