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ù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java 實戰(zhàn)練手項目之醫(yī)院預約掛號系統(tǒng)的實現(xiàn)流程
讀萬卷書不如行萬里路,只學書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SpringBoot+Maven+Vue+mysql實現(xiàn)一個醫(yī)院預約掛號系統(tǒng),大家可以在過程中查缺補漏,提升水平2021-11-11
Java使用ShardingSphere實現(xiàn)數(shù)據(jù)庫分片的策略指南
隨著業(yè)務數(shù)據(jù)量的爆炸式增長,數(shù)據(jù)庫分片作為解決大數(shù)據(jù)量存儲和查詢性能問題的核心技術,已成為現(xiàn)代分布式系統(tǒng)架構的重要組成部分,ShardingSphere作為一套開源的分布式數(shù)據(jù)庫解決方案,提供了強大的數(shù)據(jù)分片功能,本文將深入探討ShardingSphere的核心分片策略2025-08-08
spring?boot配置dubbo方式(properties)
這篇文章主要介紹了spring?boot配置dubbo方式(properties),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01
tio-http-server打包為二進制文件的實現(xiàn)及優(yōu)勢詳解
這篇文章主要為大家介紹了tio-http-server打包為二進制文件實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12
java9的JShell小工具和編譯器兩種自動優(yōu)化方法
這篇文章主要介紹了java9的JShell小工具和編譯器兩種自動優(yōu)化方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-07-07
詳解Spring Cloud Gateway基于服務發(fā)現(xiàn)的默認路由規(guī)則
這篇文章主要介紹了詳解Spring Cloud Gateway基于服務發(fā)現(xiàn)的默認路由規(guī)則,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-05-05

