【CompletableFuture 核心操作全解】详细注释版
在现代 Java 并发编程中,CompletableFuture是java.util.concurrent包中一个革命性的类。它从 Java 8 引入,将异步编程从回调地狱提升到了声明式和组合式的全新高度。本文将深入剖析其核心原理,并通过可运行代码示例全面展示其操作。## CompletableFuture 核心原理:从 Future 到 CompletableFuture要理解CompletableFuture,首先要对比传统的Future。Future代表一个异步计算的结果,但它有以下缺陷:- 无法手动完成计算(只能由异步任务完成)- 阻塞式获取结果(get()会阻塞线程)- 无法进行结果转换和组合CompletableFuture则实现了Future和CompletionStage接口。其核心原理是基于事件驱动的回调链:每个CompletableFuture内部维护一个等待该阶段完成的依赖者列表。当阶段完成(正常或异常)时,它会触发所有依赖者的回调。这使得我们可以声明式地编排异步任务,而无需手动管理线程同步。关键设计点:-complete():手动完成计算,设置结果值-completeExceptionally():手动完成计算,设置异常- 回调链通过thenApply、thenCompose等方法构建,每个方法返回一个新的CompletableFuture,形成 DAG(有向无环图)结构- 默认使用ForkJoinPool.commonPool()执行异步任务,但可自定义线程池## 创建 CompletableFuture:手动完成与异步执行CompletableFuture提供了多种创建方式,最常用的是runAsync(无返回值)和supplyAsync(有返回值)。此外,也可以直接new一个实例并手动完成。### 代码示例 1:基础创建与手动完成javaimport java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;public class CreateCompletableFutureDemo { public static void main(String[] args) throws ExecutionException, InterruptedException { // 1. 直接创建并手动完成 CompletableFuture<String> manualFuture = new CompletableFuture<>(); // 在另一个线程中手动完成它(模拟异步事件) new Thread(() -> { try { Thread.sleep(1000); manualFuture.complete("手动完成的结果"); } catch (InterruptedException e) { manualFuture.completeExceptionally(e); } }).start(); System.out.println("等待手动完成结果..."); System.out.println(manualFuture.get()); // 阻塞直到 complete() 被调用 // 2. 异步执行无返回值的任务 CompletableFuture<Void> runFuture = CompletableFuture.runAsync(() -> { System.out.println("runAsync 执行:线程 " + Thread.currentThread().getName()); try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); runFuture.get(); // 等待完成 // 3. 异步执行有返回值的任务 CompletableFuture<Integer> supplyFuture = CompletableFuture.supplyAsync(() -> { System.out.println("supplyAsync 执行:线程 " + Thread.currentThread().getName()); try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return 42; }); System.out.println("异步计算结果:" + supplyFuture.get()); // 4. 使用自定义线程池(推荐) // ExecutorService executor = Executors.newFixedThreadPool(2); // CompletableFuture<Void> customPoolFuture = CompletableFuture.runAsync(() -> { // System.out.println("使用自定义线程池"); // }, executor); // customPoolFuture.get(); // executor.shutdown(); }}运行说明:该示例展示了三种创建方式。注意manualFuture.complete()在另一个线程中调用,主线程通过get()等待并获取结果。supplyAsync自动使用ForkJoinPool.commonPool()执行任务。## 结果转换与消费:thenApply、thenAccept、thenRunCompletableFuture的核心能力之一是链式转换。thenApply将结果转换为另一个值,thenAccept消费结果但不返回新值,thenRun仅执行一个 Runnable。这些方法默认在调用线程中执行回调,但可以指定线程池。### 代码示例 2:链式转换与消费javaimport java.util.concurrent.CompletableFuture;public class ChainingDemo { public static void main(String[] args) throws Exception { // 构建异步计算链 CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { // 第一阶段:获取原始数据 try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("阶段1:获取原始数据"); return "Hello"; }).thenApplyAsync(result -> { // 第二阶段:转换结果(异步执行) System.out.println("阶段2:转换结果,线程 " + Thread.currentThread().getName()); return result.toUpperCase(); }).thenApply(result -> { // 第三阶段:再次转换(在调用线程中执行,即主线程) System.out.println("阶段3:再次转换,线程 " + Thread.currentThread().getName()); return result + " World"; }).thenAccept(finalResult -> { // 第四阶段:消费最终结果 System.out.println("最终结果:" + finalResult); }).thenRun(() -> { // 第五阶段:执行清理操作 System.out.println("清理操作完成"); }); // 等待整个链完成 future.get(); System.out.println("所有阶段执行完毕"); // 异常处理示例 CompletableFuture<Integer> exceptionDemo = CompletableFuture.supplyAsync(() -> { if (Math.random() > 0.5) { throw new RuntimeException("随机错误发生"); } return 100; }).exceptionally(ex -> { System.out.println("捕获异常:" + ex.getMessage()); return -1; // 返回默认值 }).thenApply(result -> result * 2); System.out.println("异常处理结果:" + exceptionDemo.get()); }}原理剖析:-thenApply和thenAccept会返回新的CompletableFuture,形成依赖链- 如果前一个阶段异常,后续阶段会直接跳过(除非使用exceptionally或handle处理)-thenApplyAsync将回调提交到线程池异步执行,而thenApply默认在触发线程中同步执行## 组合多个 CompletableFuture:thenCompose 与 thenCombine当需要将一个CompletableFuture的结果作为另一个异步任务的输入时,应使用thenCompose(flatMap 语义)。而thenCombine则用于将两个独立的异步结果合并。### 代码示例 3:组合与合并javaimport java.util.concurrent.CompletableFuture;public class CompositionDemo { public static void main(String[] args) throws Exception { // thenCompose:异步流水线 CompletableFuture<Integer> composedFuture = CompletableFuture.supplyAsync(() -> { System.out.println("获取用户ID..."); return 123; }).thenCompose(userId -> { // 使用 userId 启动另一个异步任务 return CompletableFuture.supplyAsync(() -> { System.out.println("根据用户ID " + userId + " 获取订单金额"); try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return 999; }); }); System.out.println("最终金额:" + composedFuture.get()); // thenCombine:合并两个独立异步结果 CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(300); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return "Hello"; }); CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return "World"; }); CompletableFuture<String> combinedFuture = future1.thenCombine(future2, (s1, s2) -> { return s1 + " " + s2 + "!"; }); System.out.println("合并结果:" + combinedFuture.get()); // allOf:等待所有完成 CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2); allFutures.get(); // 阻塞直到两者都完成 System.out.println("所有任务完成"); // anyOf:任意一个完成即可 CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(future1, future2); System.out.println("任意一个完成的结果:" + anyFuture.get()); }}关键区别:-thenApply返回CompletableFuture<U>,其中 U 是转换结果类型-thenCompose返回CompletableFuture<U>,但其函数返回CompletableFuture<U>,避免嵌套CompletableFuture<CompletableFuture<U>>-thenCombine等待两个 future 都完成后合并结果,不要求它们有依赖关系## 异常处理与最终操作:handle、whenComplete异常处理是异步编程的难点。exceptionally只处理异常,handle无论正常或异常都执行,whenComplete类似但不返回新值。### 代码示例 4:异常处理全解javaimport java.util.concurrent.CompletableFuture;public class ErrorHandlingDemo { public static void main(String[] args) throws Exception { // handle:无论成功失败都执行,可以转换结果 CompletableFuture<String> handleFuture = CompletableFuture.supplyAsync(() -> { if (Math.random() > 0.5) { throw new RuntimeException("业务异常"); } return "成功结果"; }).handle((result, ex) -> { if (ex != null) { System.out.println("handle 捕获异常:" + ex.getMessage()); return "默认恢复值"; } return result + "(经过handle处理)"; }); System.out.println("handle 最终结果:" + handleFuture.get()); // whenComplete:类似 try-finally,不改变结果 CompletableFuture<Integer> whenCompleteFuture = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(200); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return 100; }).whenComplete((result, ex) -> { if (ex != null) { System.out.println("whenComplete 发现异常:" + ex.getMessage()); } else { System.out.println("whenComplete 正常完成,结果:" + result); } }); System.out.println("whenComplete 返回结果:" + whenCompleteFuture.get()); // 超时控制(Java 9+) CompletableFuture<Integer> timeoutFuture = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(5000); // 模拟耗时操作 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return 200; }); // 注意:orTimeout 是 Java 9 特性,如果使用 Java 8 需手动实现 // timeoutFuture.orTimeout(1, TimeUnit.SECONDS); // 使用 getNow 避免阻塞 Integer result = timeoutFuture.getNow(-1); // 如果未完成返回 -1 System.out.println("快速获取结果:" + result); }}## 总结CompletableFuture是 Java 异步编程的瑞士军刀,通过事件驱动机制实现了声明式的异步编排。本文深入剖析了其核心原理——基于回调链和完成事件触发,并通过四个可运行代码示例展示了以下关键操作:1.创建:runAsync、supplyAsync和手动complete2.转换与消费:thenApply、thenAccept、thenRun及其异步变体3.组合:thenCompose(流水线)、thenCombine(合并)、allOf/anyOf(聚合)4.异常处理:exceptionally、handle、whenComplete理解这些操作的关键在于:每个方法都返回新的CompletableFuture,形成不可变的事件链;异步执行默认使用公共线程池,但可通过Async后缀的方法指定自定义执行器。在实际项目中,合理使用CompletableFuture可以显著提升系统的响应性和吞吐量,但需要注意线程池大小和异常传播,避免死锁和未处理异常。