CompletableFuture 是 Java 中实现多任务并行执行并选取首个完成结果的理想工具,支持 anyOf、applyToEither 和 orTimeout 等方法,兼顾类型安全、超时控制与异常处理。

在 Java 多线程中,CompletableFuture 是实现“多任务并行执行,并选取首个完成结果”的理想工具。它天然支持异步、非阻塞、组合式编程,无需手动管理线程或同步逻辑,就能优雅地完成“谁先返回就用谁”的需求。
用 anyOf 获取任意一个完成的任务结果
CompletableFuture.anyOf() 接收多个 CompletableFuture,返回一个新的 CompletableFuture,当其中任一任务完成(正常或异常)时即触发。注意:它返回的是 Object 类型,需强制转换并处理可能的异常情况。
- 适用于“只要一个结果,不在乎是哪一个”的场景,比如调用多个相同接口的降级服务、并发查询缓存与数据库
- 返回值需用
join()获取,再做类型转换;若首个完成的是异常任务,会抛出CompletionException - 示例:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> { sleep(2000); return "result1"; }); CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> { sleep(1000); return "result2"; }); CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> { sleep(1500); return "result3"; }); <p>Object result = CompletableFuture.anyOf(task1, task2, task3).join(); System.out.println((String) result); // 输出 "result2"
用 applyToEither 组合两个任务的优先结果
当只有两个异步任务,且希望对“先完成的那个”直接进行后续处理时,applyToEither 更简洁、类型安全。它返回新 CompletableFuture,自动继承先完成任务的结果类型。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 避免
anyOf的类型擦除和强转麻烦,编译期即可校验类型 - 只支持两个任务,但链式调用可嵌套扩展(如
a.applyToEither(b, f).applyToEither(c, g),但语义变复杂,不推荐) - 示例:
CompletableFuture<Integer> fast = CompletableFuture.supplyAsync(() -> { sleep(500); return 42; }); CompletableFuture<Integer> slow = CompletableFuture.supplyAsync(() -> { sleep(2000); return 99; }); <p>Integer winner = fast.applyToEither(slow, x -> x * 2).join(); // 得到 84立即学习“Java免费学习笔记(深入)”;
用 orTimeout + exceptionally 控制超时兜底
实际业务中,“选最快”往往隐含超时要求。可为每个任务设置独立超时,并用 exceptionally 提供默认值,再结合 anyOf 或 applyToEither 使用。
-
orTimeout()会在指定时间后以TimeoutException完成该 future -
exceptionally()可将超时异常转为默认结果(如空值、缓存数据、降级响应) - 示例(带超时的竞速查询):
CompletableFuture<String> apiA = CompletableFuture.supplyAsync(() -> callApi("A")) .orTimeout(1, TimeUnit.SECONDS) .exceptionally(t -> "fallback_A"); <p>CompletableFuture<String> apiB = CompletableFuture.supplyAsync(() -> callApi("B")) .orTimeout(1, TimeUnit.SECONDS) .exceptionally(t -> "fallback_B");</p><p>String best = CompletableFuture.anyOf(apiA, apiB).join().toString();
注意线程池与异常传播细节
CompletableFuture 默认使用 ForkJoinPool.commonPool(),但在 Web 或高并发场景下建议显式传入自定义线程池,避免资源争抢。同时,异常不会静默吞掉——未处理的异常会导致 anyOf 返回的 future 以异常完成,调用 join() 时抛出 CompletionException,务必捕获或用 handle/exceptionally 显式处理。
- 推荐创建专用线程池:
new ThreadPoolExecutor(5, 20, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue(100)) - 避免在 lambda 中直接 throw 受检异常;若需,用 try-catch 包裹后转为运行时异常
- 组合多个 future 时,优先用
thenApply/handle而非join()阻塞,保持异步流畅通

















