CompletableFuture異步多線程是真的優(yōu)雅
今天給大家介紹一個非常有意思的Java編程中的奇淫巧技——用CompletableFuture來做異步編程。
先說一下異步編程是啥,以及一般都是什么時候用他們。
其實大家比較常規(guī)習慣的都是用springboot+ssm+springcloud alibaba技術(shù)體系去開發(fā)一些web系統(tǒng),然后里面填充各種crud代碼,相信看這篇文章的朋友應該都差不多,畢竟天下苦crud久已,進軟件開發(fā)行業(yè)之前覺得很高大上,進來以后發(fā)現(xiàn)技術(shù)都熟悉的差不多了,更多的都是在干crud。
那其實在這種常規(guī)性的crud類的系統(tǒng)和技術(shù)體系下,一般我們都不需要去做什么并發(fā)編程、異步編程之類的,因為一般系統(tǒng)原理都是springboot拉起來一個內(nèi)嵌的web server,比如tomcat,然后對外接http請求,多線程并發(fā)處理請求,每個線程都是調(diào)用你的controller、service、dao和sql語句,是不是?
而且其實大家平時用spring的話,所有的spring bean都是做成單例的,而且單例一般都是無狀態(tài)的,就是大家不會在內(nèi)存里放什么數(shù)據(jù),也就避免了多線程并發(fā)的安全問題,然后我們平時也就不用在crud系統(tǒng)里關(guān)注多線程問題了。
所以其實我們寫的代碼都是順序流的模式,來一個請求,各種業(yè)務邏輯處理,調(diào)用service,dao,執(zhí)行各種sql語句,然后最后都是面向數(shù)據(jù)庫做增刪改查,所以根本沒機會也不需要使用并發(fā)編程和異步編程。
當然這種模式在crud業(yè)務類系統(tǒng)里還是不錯的,畢竟方便,寫業(yè)務就可以了,不要去關(guān)注復雜的多線程并發(fā)和異步化的問題。
所以并發(fā)編程和異步編程指的是什么呢?并發(fā)編程其實是指比如你寫了一個系統(tǒng),你的系統(tǒng)代碼里不是執(zhí)行那種crud順序流代碼,而是自己搞一個class,繼承Thread,自己實現(xiàn)一個線程類,然后你的系統(tǒng)代碼里拿到一個請求不是直接去crud,而是啟動一個Thread線程去并發(fā)運行起來,這個線程不就會異步的去執(zhí)行一些操作了。
雖然大家一般真的很少在業(yè)務系統(tǒng)里用這種并發(fā)編程和異步編程的模式,但是代碼里確實是可以這么玩的。
好,那一般什么時候需要在代碼里開一些線程去并發(fā)運行,異步化的運行呢?不幸的消息是,crud系統(tǒng)真的很少用,一般其實都是中間件類的系統(tǒng)會大量的運用并發(fā)編程的知識,各種請求都是異步化的執(zhí)行,比如說大家可以去看看rocketmq、elasticsearch這一類中間件的源碼,他們會經(jīng)常用到。
或者說大家在自己公司里研發(fā)一些非crud類的系統(tǒng),比如說一些公司內(nèi)自研的一些底層系統(tǒng),基礎系統(tǒng),中間件系統(tǒng),其實也會經(jīng)常用到并發(fā)編程,也就是異步編程的模式,那如果大家未來有可能用到異步編程的話,建議還是來了解一下今天的知識點,因為傳統(tǒng)的異步編程其實控制你開的那個線程其實真的很麻煩。
但是用了CompletableFuture之后,對你開出來的多線程并發(fā)任務,你其實是可以很好的去控制他們的,一起開始今天的旅程吧。
在Java的世界里,處理異步和多線程任務一直是個讓人頭疼的問題。傳統(tǒng)的線程創(chuàng)建和管理方式,不僅代碼繁瑣,還容易出錯。但是,自從Java 8推出了CompletableFuture這個神器,一切都變得不一樣了。它以一種極其優(yōu)雅的方式,解決了異步編程的諸多痛點。今天,咱們就來聊聊這個CompletableFuture,看看它到底是如何讓異步多線程編程變得如此優(yōu)雅的。
一、異步編程的痛點
在CompletableFuture出現(xiàn)之前,Java的異步編程主要有兩種方式:通過Future接口和實現(xiàn)Callable接口。但是,這兩種方式都存在一些問題。
1、阻塞和輪詢:使用Future.get()方法獲取異步結(jié)果時,如果結(jié)果還沒有準備好,當前線程會被阻塞。為了避免阻塞,我們通常會使用輪詢的方式檢查結(jié)果是否準備好,但這種方式會浪費CPU資源。
2、無法組合多個異步任務:在實際開發(fā)中,我們經(jīng)常需要組合多個異步任務的結(jié)果。但是,使用傳統(tǒng)的Future和Callable,很難實現(xiàn)復雜的異步任務組合邏輯。
3、異常處理不便:當異步任務出現(xiàn)異常時,傳統(tǒng)的處理方式是通過Future.get()方法捕獲異常,但這種方式不夠靈活,也不便于異步任務的錯誤恢復。
二、CompletableFuture的優(yōu)雅之處
CompletableFuture是Java 8引入的一個新的異步編程工具,它解決了傳統(tǒng)異步編程方式的諸多痛點,讓異步多線程編程變得更加優(yōu)雅和便捷。
1、非阻塞的異步結(jié)果獲?。篊ompletableFuture提供了非阻塞的異步結(jié)果獲取方式。你可以通過thenApply、thenAccept、thenRun等方法,在異步任務完成時執(zhí)行特定的操作,而無需阻塞當前線程。
2、靈活的異步任務組合:CompletableFuture提供了豐富的API,支持多種異步任務的組合方式。你可以使用thenCombine、thenAcceptBoth、runAfterBoth等方法,將兩個異步任務的結(jié)果進行組合;或者使用allOf、anyOf等方法,等待多個異步任務完成。
3、便捷的異常處理:CompletableFuture提供了exceptionally方法,用于處理異步任務中出現(xiàn)的異常。你可以在這個方法中定義異常的處理邏輯,使得異步任務的錯誤恢復變得更加便捷。
4、鏈式調(diào)用和流式處理:CompletableFuture的方法調(diào)用支持鏈式操作,你可以將多個異步任務串聯(lián)起來,形成一個處理流程。這種鏈式調(diào)用和流式處理的方式,使得異步編程的代碼更加簡潔和易讀。
三、CompletableFuture的使用示例
下面,我們通過一些具體的示例,來看看CompletableFuture是如何優(yōu)雅地解決異步多線程編程的問題的。
示例1:異步執(zhí)行任務并獲取結(jié)果
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模擬異步任務
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello, CompletableFuture!";
});
// 非阻塞方式獲取結(jié)果
future.thenAccept(System.out::println);
在這個示例中,我們使用supplyAsync方法異步執(zhí)行了一個任務,并返回了一個CompletableFuture對象。然后,我們使用thenAccept方法,在異步任務完成時打印結(jié)果。這種方式避免了阻塞和輪詢,使得代碼更加簡潔和高效。
示例2:組合多個異步任務的結(jié)果
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
future1.thenCombine(future2, (result1, result2) -> result1 + " " + result2)
.thenAccept(System.out::println);
在這個示例中,我們使用thenCombine方法將兩個異步任務的結(jié)果進行了組合。當兩個異步任務都完成時,它們的結(jié)果會被拼接成一個新的字符串,并打印出來。這種方式使得異步任務的組合變得更加靈活和便捷。
示例3:處理異步任務中的異常
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
if (true) { // 假設這里有個條件判斷,滿足時拋出異常
throw new RuntimeException("Oops!");
}
return "Normal result";
});
future.exceptionally(ex -> "Fallback result")
.thenAccept(System.out::println);
在這個示例中,我們使用exceptionally方法處理了異步任務中出現(xiàn)的異常。當異步任務拋出異常時,我們會返回一個備用結(jié)果,并打印出來。這種方式使得異步任務的錯誤恢復變得更加容易和可控。
四、CompletableFuture的高級特性
除了上述的基本用法外,CompletableFuture還提供了一些高級特性,進一步增強了其異步編程的能力。
1、自定義線程池:默認情況下,CompletableFuture會使用ForkJoinPool.commonPool()作為線程池來執(zhí)行異步任務。但是,你也可以通過supplyAsync、runAsync等方法的重載版本,自定義線程池來執(zhí)行異步任務。
2、完成時的回調(diào):CompletableFuture提供了whenComplete、whenCompleteAsync等方法,允許你在異步任務完成時執(zhí)行特定的回調(diào)操作。這些回調(diào)操作可以處理正常結(jié)果,也可以處理異常情況。
3、結(jié)果計算完成時的通知:CompletableFuture還提供了thenRun方法,允許你在異步任務的結(jié)果計算完成時執(zhí)行特定的操作。這個方法不關(guān)心異步任務的結(jié)果,只關(guān)心任務是否完成。
4、等待多個異步任務完成:CompletableFuture提供了allOf、anyOf等靜態(tài)方法,用于等待多個異步任務完成。allOf會等待所有任務完成,而anyOf則只等待其中一個任務完成。
五、總結(jié)
CompletableFuture是Java 8引入的一個強大的異步編程工具,它以一種極其優(yōu)雅的方式解決了傳統(tǒng)異步編程方式的諸多痛點。通過使用CompletableFuture,我們可以更加便捷地實現(xiàn)異步多線程編程,提高代碼的可讀性和可維護性。同時,CompletableFuture還提供了豐富的高級特性,進一步增強了其異步編程的能力。因此,如果你還在為異步多線程編程而苦惱,不妨嘗試一下CompletableFuture,相信它會給你帶來全新的編程體驗。