Java中的CompletableFuture核心用法和常見場景
1、引言
CompletableFuture 是 Java 8 引入的一個非常強大的異步編程工具,屬于 java.util.concurrent 包。它不僅支持異步執(zhí)行任務,還支持任務的組合、異常處理、回調等豐富的操作。下面我會詳細介紹 CompletableFuture 的核心用法和常見場景。
2. 基本概念
- Future:早期的異步結果表示,功能有限,只能通過
get()阻塞獲取結果。 - CompletableFuture:增強版的 Future,支持鏈式異步編程、組合、異常處理、回調等。
3. 創(chuàng)建 CompletableFuture
3.1. 手動創(chuàng)建
CompletableFuture<String> future = new CompletableFuture<>();
// 可以手動完成
future.complete("Hello");3.2. 通過靜態(tài)工廠方法
supplyAsync:有返回值,異步執(zhí)行runAsync:無返回值,異步執(zhí)行
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
// 模擬耗時操作
return "Hello CompletableFuture";
});
CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
// 執(zhí)行某些操作
});4. 獲取結果
get():阻塞等待結果join():類似于get(),但遇到異常會拋出 unchecked 異常
String result = future1.get(); // 可能拋出異常 String result2 = future1.join(); // RuntimeException
5. 回調和鏈式操作
5.1. thenApply / thenApplyAsync
對結果進行轉換(有返回值)
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 100)
.thenApply(i -> i + 10); // 結果為1105.2. thenAccept / thenAcceptAsync
對結果做處理,無返回值
CompletableFuture.supplyAsync(() -> "hello")
.thenAccept(s -> System.out.println(s));5.3. thenRun / thenRunAsync
無參數(shù)、無返回值,僅執(zhí)行后續(xù)操作
CompletableFuture.supplyAsync(() -> "hello")
.thenRun(() -> System.out.println("任務執(zhí)行完畢"));6. 任務組合
5.1. thenCombine / thenCombineAsync
兩個任務都完成后,合并結果
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> 10); CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> 20); CompletableFuture<Integer> result = f1.thenCombine(f2, (a, b) -> a + b); // 30
6.2. thenCompose / thenComposeAsync
任務依賴,前一個結果作為后一個輸入
CompletableFuture<String> f = CompletableFuture.supplyAsync(() -> "Hello")
.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World"));6.3. allOf / anyOf
等待多個任務全部/任一完成
CompletableFuture<Void> all = CompletableFuture.allOf(f1, f2); all.join(); // 等待全部完成 CompletableFuture<Object> any = CompletableFuture.anyOf(f1, f2); Object fastest = any.join(); // 任意一個完成即可
7. 異常處理
7.1. exceptionally
捕獲異常,返回默認值
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("出錯了");
}).exceptionally(e -> {
System.out.println(e.getMessage());
return 0;
});7.2. handle / handleAsync
無論成功或失敗都處理
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("出錯了");
}).handle((result, ex) -> {
if (ex != null) {
System.out.println(ex.getMessage());
return 0;
}
return result;
});8. 自定義線程池
默認使用 ForkJoinPool.commonPool(),可以自定義線程池:
ExecutorService executor = Executors.newFixedThreadPool(2); CompletableFuture.supplyAsync(() -> "hello", executor);
9. 實用示例
9.1. 多個異步任務并發(fā)執(zhí)行,最后匯總
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "A");
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "B");
CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> "C");
CompletableFuture<Void> all = CompletableFuture.allOf(f1, f2, f3);
all.thenRun(() -> {
try {
System.out.println(f1.get() + f2.get() + f3.get());
} catch (Exception e) {
e.printStackTrace();
}
});10. 注意事項
- 盡量避免阻塞(如
get()),推薦使用回調。 - 注意線程池資源,合理分配,避免 OOM。
- 處理好異常,避免線程池線程被異常吞掉。
11. 進階用法
11.1. 串聯(lián)和并聯(lián)任務
串聯(lián)(依賴關系)
當一個任務的結果依賴于另一個任務時,使用 thenCompose:
CompletableFuture<String> getUserId = CompletableFuture.supplyAsync(() -> "user123");
CompletableFuture<String> getUserInfo = getUserId.thenCompose(id ->
CompletableFuture.supplyAsync(() -> "用戶信息:" + id)
);并聯(lián)(聚合結果)
多個任務并發(fā)執(zhí)行,最后聚合結果:
CompletableFuture<Integer> t1 = CompletableFuture.supplyAsync(() -> 1);
CompletableFuture<Integer> t2 = CompletableFuture.supplyAsync(() -> 2);
CompletableFuture<Integer> t3 = CompletableFuture.supplyAsync(() -> 3);
CompletableFuture<List<Integer>> all = CompletableFuture.allOf(t1, t2, t3)
.thenApply(v -> {
List<Integer> result = new ArrayList<>();
result.add(t1.join());
result.add(t2.join());
result.add(t3.join());
return result;
});11.2. 超時控制
Java 9 后,CompletableFuture 增加了超時相關方法:
future.orTimeout(3, TimeUnit.SECONDS)
.exceptionally(ex -> {
System.out.println("超時啦");
return null;
});或自己實現(xiàn):
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
Thread.sleep(5000);
return "hello";
});
try {
String result = future.get(2, TimeUnit.SECONDS); // 2秒超時
} catch (TimeoutException e) {
System.out.println("超時了");
}11.3. 異步流水線
你可以鏈式地組合多個異步操作,形成“流水線”:
CompletableFuture.supplyAsync(() -> "A")
.thenApply(s -> s + "B")
.thenApply(s -> s + "C")
.thenAccept(System.out::println); // 輸出 ABC11.4. 處理異常和兜底方案
推薦使用 handle 或 exceptionally 做兜底:
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("出錯了");
})
.handle((result, ex) -> ex == null ? result : -1);11.5. 自定義線程池的好處
- 控制線程數(shù)量,避免公共線程池被占滿。
- 適合高并發(fā)、IO密集型場景。
ExecutorService executor = Executors.newFixedThreadPool(10); CompletableFuture.supplyAsync(() -> "業(yè)務邏輯", executor);
12. 實戰(zhàn)場景舉例
12.1. 微服務并發(fā)調用
假設要并發(fā)調用三個微服務接口,最后聚合結果:
CompletableFuture<String> api1 = CompletableFuture.supplyAsync(() -> callApi1());
CompletableFuture<String> api2 = CompletableFuture.supplyAsync(() -> callApi2());
CompletableFuture<String> api3 = CompletableFuture.supplyAsync(() -> callApi3());
CompletableFuture<Void> all = CompletableFuture.allOf(api1, api2, api3);
all.thenAccept(v -> {
String r1 = api1.join();
String r2 = api2.join();
String r3 = api3.join();
System.out.println("聚合結果:" + r1 + r2 + r3);
});12.2. 異步寫數(shù)據(jù)庫+異步發(fā)消息
CompletableFuture<Void> saveDb = CompletableFuture.runAsync(() -> saveToDb());
CompletableFuture<Void> sendMsg = CompletableFuture.runAsync(() -> sendMsg());
CompletableFuture.allOf(saveDb, sendMsg)
.thenRun(() -> System.out.println("所有操作完成"));13. 常見問題與建議
- 線程池泄漏:線程池要合理關閉,避免資源泄漏。
- 異常未處理:建議所有鏈路最后加
.exceptionally或.handle。 - 阻塞等待:盡量用回調而不是
get()或join()。 - 鏈式操作:推薦鏈式編程,代碼更清晰。
總結
CompletableFuture 是 Java 異步編程的利器,支持豐富的組合、回調和異常處理能力。合理使用可以極大提升程序的并發(fā)能力和響應速度。
到此這篇關于Java中的CompletableFuture核心用法和常見場景的文章就介紹到這了,更多相關java completablefuture使用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring線程池ThreadPoolExecutor配置并且得到任務執(zhí)行的結果
今天小編就為大家分享一篇關于Spring線程池ThreadPoolExecutor配置并且得到任務執(zhí)行的結果,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03
Spring?MVC中JSON數(shù)據(jù)處理方式實戰(zhàn)案例
Spring MVC是個靈活的框架,返回JSON數(shù)據(jù)的也有很多五花八門的方式,下面這篇文章主要給大家介紹了關于Spring?MVC中JSON數(shù)據(jù)處理方式的相關資料,需要的朋友可以參考下2024-01-01
詳談hibernate,jpa與spring?data?jpa三者之間的關系
這篇文章主要介紹了hibernate,jpa與spring?data?jpa三者之間的關系,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
Java java.lang.InstantiationException異常案例詳解
這篇文章主要介紹了Java java.lang.InstantiationException異常案例詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下2021-08-08
解決IDEA 2020.1版本 maven Test命令出現(xiàn)導包錯誤的問題
這篇文章主要介紹了IDEA 2020.1版本 maven Test命令出現(xiàn)導包錯誤的問題及解決方法,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08

