Java中如何使用非阻塞異步編程——CompletableFuture

分享一波:程序員賺外快-必看的巔峰干貨

對于Node開發者來說,非阻塞異步編程是他們引以為傲的地方。而在JDK8中,也引入了非阻塞異步編程的概念。所謂非阻塞異步編程,就是一種不需要等待返回結果的多線程的回調方法的封裝。使用非阻塞異步編程,可以很大程度上解決高并發場景的各種問題,提高程序的運行效率。
為什么要使用非阻塞異步編程

在jdk8之前,我們使用java的多線程編程,一般是通過Runnable中的run方法進行的。這種方法有個明顯的缺點:沒有返回值。這時候,大家會使用Callable+Future的方式去實現,代碼如下。

public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future stringFuture = executor.submit(new Callable() {
@Override
public String call() throws Exception {
Thread.sleep(2000);
return “async thread”;
}
});
Thread.sleep(1000);
System.out.println(“main thread”);
System.out.println(stringFuture.get());
}

這無疑是對高并發訪問的一種緩沖方法。這種方式有一個致命的缺點就是阻塞式調用,當調用了get方法之后,會有大量的時間耗費在等待返回值之中。

不管怎么看,這種做法貌似都不太妥當,至少在代碼美觀性上就看起來很蛋疼。而且某些場景無法使用,比如:

多個異步線程執行時間可能不一致,我們的主線程不能一直等著。
兩個異步任務之間相互獨立,但是第二個依賴第一個的執行結果

在這種場景下,CompletableFuture的優勢就展現出來了 。同時,CompletableFuture的封裝中使用了函數式編程,這讓我們的代碼顯得更加簡潔、優雅。

不了解函數式編程的朋友,可以參考我之前的博客。JDK8新特性
CompletableFuture使用詳解
runAsync和supplyAsync方法

CompletableFuture提供了四個靜態方法來創建一個異步操作。

public static CompletableFuture runAsync(Runnable runnable)
public static CompletableFuture runAsync(Runnable runnable, Executor executor)
public static CompletableFuture supplyAsync(Supplier supplier)
public static CompletableFuture supplyAsync(Supplier supplier, Executor executor)

沒有指定Executor的方法會使用ForkJoinPool.commonPool() 作為它的線程池執行異步代碼。如果指定線程池,則使用指定的線程池運行。以下所有的方法都類同。

runAsync方法不支持返回值。
supplyAsync可以支持返回值。

代碼示例

/*** 無返回值** @throws Exception*/
@Test
public void testRunAsync() throws Exception {CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (Exception ignored) {}System.out.println("run end ...");});future.get();
}/*** 有返回值** @throws Exception*/
@Test
public void testSupplyAsync() throws Exception {CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> {System.out.println("run end...");return System.currentTimeMillis();});Long time = future.get();System.out.println(time);
}

計算結果完成時的回調方法

當CompletableFuture的計算結果完成,或者拋出異常的時候,可以執行特定的操作。

public CompletableFuture whenComplete(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
public CompletableFuture exceptionally(Function<Throwable,? extends T> fn)

這里需要說的一點是,whenComplete和whenCompleteAsync的區別。

whenComplete:使用執行當前任務的線程繼續執行whenComplete的任務。
whenCompleteAsync:使用新的線程執行任務。
exceptionally:執行出現異常時,走這個方法。

代碼示例

/*** 當CompletableFuture的計算結果完成,或者拋出異常的時候,可以執行特定的Action。* whenComplete:是執行當前任務的線程執行繼續執行 whenComplete 的任務。* whenCompleteAsync:是執行把 whenCompleteAsync 這個任務繼續提交給線程池來進行執行。* exceptionally:執行出現異常時,走這個方法** @throws Exception*/
@Test
public void testWhenComplete() throws Exception {CompletableFuture.runAsync(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("運行結束");}).whenComplete((t, action) -> {System.out.println("執行完成");}).exceptionally(t -> {System.out.println("出現異常:" + t.getMessage());return null;});TimeUnit.SECONDS.sleep(2);
}

thenApply

當一個線程依賴另一個線程時,可以使用thenApply方法把這兩個線程串行化,第二個任務依賴第一個任務的返回值。

代碼示例

/*** 當一個線程依賴另一個線程時,可以使用 thenApply 方法來把這兩個線程串行化。* 第二個任務依賴第一個任務的結果** @throws Exception*/
@Test
public void testThenApply() throws Exception {CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> {long result = new Random().nextInt();System.out.println("result:" + result);return result;}).thenApply(t -> {long result = t * 5;System.out.println("result2:" + result);return result;});Long result = future.get();System.out.println(result);
}

handle

handle是執行任務完成時對結果的處理。與thenApply方法處理方式基本一致,

不同的是,handle是在任務完成后執行,不管這個任務是否出現了異常,而thenApply只可以執行正常的任務,任務出現了異常則不執行。

代碼示例

/*** handle 是執行任務完成時對結果的處理。* handle 方法和 thenApply 方法處理方式基本一樣。* 不同的是 handle 是在任務完成后再執行,還可以處理異常的任務。* thenApply 只可以執行正常的任務,任務出現異常則不執行 thenApply 方法。** @throws Exception*/
@Test
public void testHandle() throws Exception {CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {int i = 10 / 0;return i;}).handle((p, t) -> {int result = -1;if (t == null) {result = p * 2;} else {System.out.println(t.getMessage());}return result;});System.out.println(future.get());
}

thenAccept

thenAccept用于接收任務的處理結果,并消費處理,無返回結果。

代碼示例

/*** 接收任務的處理結果,并消費處理,無返回結果。** @throws Exception*/
@Test
public void testThenAccept() throws Exception {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {return new Random().nextInt();}).thenAccept(num -> {System.out.println(num);});System.out.println(future.get());
}

thenRun

上個任務執行完之后再執行thenRun的任務,二者只存在先后執行順序的關系,后者并不依賴前者的計算結果,同時,沒有返回值。

代碼示例

/*** 該方法同 thenAccept 方法類似。不同的是上個任務處理完成后,并不會把計算的結果傳給 thenRun 方法。* 只是處理玩任務后,執行 thenRun 的后續操作。** @throws Exception*/
@Test
public void testThenRun() throws Exception {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {return new Random().nextInt();}).thenRun(() -> {System.out.println("進入了thenRun");});System.out.println(future.get());
}

thenCombine

thenCombine會把兩個CompletableFuture的任務都執行完成后,把兩個任務的返回值一塊交給thenCombine處理(有返回值)。

代碼示例

/*** thenCombine 會把 兩個 CompletableFuture 的任務都執行完成后* 把兩個任務的結果一塊交給 thenCombine 來處理。** @throws Exception*/
@Test
public void testThenCombine() throws Exception {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {return "hello";}).thenCombine(CompletableFuture.supplyAsync(() -> {return "world";}), (t1, t2) -> {return t1 + " " + t2;});System.out.println(future.get());
}

thenAcceptBoth

當兩個CompletableFuture都執行完成后,把結果一塊交給thenAcceptBoth處理(無返回值)

代碼示例

/*** 當兩個 CompletableFuture 都執行完成后* 把結果一塊交給thenAcceptBoth來進行消耗** @throws Exception*/
@Test
public void testThenAcceptBoth() throws Exception {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {return "hello";}).thenAcceptBoth(CompletableFuture.supplyAsync(() -> {return "world";}), (t1, t2) -> {System.out.println(t1 + " " + t2);});System.out.println(future.get());
}

applyToEither

兩個CompletableFuture,誰執行返回的結果快,就用哪個的結果進行下一步操作(有返回值)。

代碼示例

/*** 兩個CompletableFuture,誰執行返回的結果快,我就用那個CompletionStage的結果進行下一步的轉化操作** @throws Exception*/
@Test
public void testApplyToEither() throws Exception {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}return "hello";}).applyToEither(CompletableFuture.supplyAsync(() -> {return "world";}), (t) -> {return t;});System.out.println(future.get());
}

acceptEither

兩個CompletableFuture,誰執行返回的結果快,就用哪個的結果進行下一步操作(無返回值)。

代碼示例

/*** 兩個CompletableFuture,誰執行返回的結果快,我就用那個CompletionStage的結果進行下一步的消耗操作。** @throws Exception*/
@Test
public void testAcceptEither() throws Exception {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {return "hello";}).acceptEither(CompletableFuture.supplyAsync(() -> {return "world";}), t1 -> {System.out.println(t1);});System.out.println(future.get());
}

runAfterEither

兩個CompletableFuture,任何一個完成了都會執行下一步操作

代碼示例

/*** 兩個CompletableFuture,任何一個完成了都會執行下一步的操作** @throws Exception*/
@Test
public void testRunAfterEither() throws Exception {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {return "hello";}).runAfterEither(CompletableFuture.supplyAsync(() -> {return "world";}), () -> {System.out.println("執行完了");});System.out.println(future.get());
}

runAfterBoth

兩個CompletableFuture,都完成了才會執行下一步操作。

代碼示例

/*** 兩個CompletableFuture,都完成了計算才會執行下一步的操作** @throws Exception*/
@Test
public void testRunAfterBoth() throws Exception {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {return "hello";}).runAfterBoth(CompletableFuture.supplyAsync(() -> {return "world";}), () -> {System.out.println("執行完了");});System.out.println(future.get());
}

thenCompose

thenCompose方法允許對兩個CompletableFuture進行流水線操作,當第一個操作完成時,將其結果作為參數傳遞給第二個操作。

代碼示例

/*** thenCompose 方法允許你對兩個 CompletableFuture 進行流水線操作,* 第一個操作完成時,將其結果作為參數傳遞給第二個操作。* @throws Exception*/
@Test
public void testThenCompose() throws Exception {CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {int t = new Random().nextInt();System.out.println(t);return t;}).thenCompose(param -> {return CompletableFuture.supplyAsync(() -> {int t = param * 2;System.out.println("t2=" + t);return t;});});System.out.println(future.get());
}

結語

CompletableFuture是jdk8中新增的一個特性,特點是非阻塞異步編程。合理的使用非阻塞異步編程,比如將兩步關聯不大的操作并行處理,可以優化代碼的執行效率。同時,在高并發場景下,CompletableFuture也可以進行有效的性能優化。
*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/536998.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536998.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536998.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

城市運行一網統管_【宣傳活動】持續開展城市運行“一網統管”建設宣傳活動...

為進一步推進本鎮城市運行“一網統管”建設工作&#xff0c;提高城市治理能力和治理水平&#xff0c;提升社會各界的知曉度和參與度&#xff0c;激發職能部門人員、黨員、群眾參與“一網統管”工作的熱情。9月10日&#xff0c;鎮網格中心于福泉居委會議室開展“推進城市運行‘一…

Java如何只使用位運算實現加減乘除

分享一波:程序員賺外快-必看的巔峰干貨 前言 接前面一篇博客&#xff0c;這又是某個公司的奇葩面試題&#xff08;都說了到底是哪家公司才會出這種沒營養的面試題&#xff09;。不過吐槽歸吐槽&#xff0c;這個題目還是有點學問的&#xff0c;比前面那個 不使用比較運算符如何…

Netweaver里某個software component和C4C的版本

有同事問如何通過代碼的方式獲得Netweaver里某個Software component的版本信息&#xff0c;以及Cloud for Customer&#xff08;C4C&#xff09;的版本信息。 Netweaver 點了Detail按鈕后&#xff1a; 這些版本信息存在表CVERS里&#xff1a; C4C C4C的版本號在Help->About …

pmc訂單表格_復工了,讀一則“如何提升訂單準交率和生產效率”的真實故事

故事發生在中國南方小鎮上一個做辦公家具的公司……家具公司創建于1995年&#xff0c;是一家集研發、生產、銷售、服務為一體的現代辦公家具、酒店家具制造企業。主要產品有實木班臺系列、會議臺系列、職員桌系列、屏風系列、沙發系列、辦公座椅、酒店家具系列。在省外還有兩個…

GET和POST請求到底有什么區別?

分享一波:程序員賺外快-必看的巔峰干貨 看到這個標題&#xff0c;想必大部分人都已經想關掉這篇博客了。先別急&#xff0c;你真的知道這兩個的區別嗎&#xff1f; 做過WEB開發的朋友可能很熟悉&#xff0c;看到這個問題能立馬脫口而出二者的區別。 GET在瀏覽器回退時是無害的…

有贊電商云應用框架設計

背景 有贊是 SaaS 公司&#xff0c;向商家提供了全方位的軟件服務&#xff0c;支撐商家進行采購、店鋪、商品、營銷、訂單、物流等等管理服務。 在這個軟件服務里&#xff0c;能夠滿足大部分的商家&#xff0c;為商家保駕護航。 但是很多大商家往往會有自己的特殊需求&#xff…

vivado 如何創建工程模式_基于Vivado的FPGA高性能開發研修班2019年8月30日上海舉行...

一、課程介紹&#xff1a;從7系列FPGA開始&#xff0c;Xilinx提出了Vivado Design Suite設計軟件&#xff0c;提供全新構建的SoC 增強型、以 IP 和系統為中心的下一代開發環境&#xff0c;以解決系統級集成和實現的生產力瓶頸。同時&#xff0c;Xilinx專門針對Vivado推出了Ultr…

程序員的自我修養——遠離“外包思維”

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 在我們做開發的日子里&#xff0c;不免會進行跳槽&#xff0c;跳來跳去公司無非就分成兩大類——互聯網公司、外包公司。當然我們本次討論的并…

英特爾為 Kubernetes 推出分布式深度學習平臺:Nauta

2019獨角獸企業重金招聘Python工程師標準>>> 隨著人工智能的發展&#xff0c;深度學習的價值不斷增長&#xff0c;但實現它可能是一個復雜耗時的過程。英特爾(Intel)正尋求通過其在 Kubernetes 進行分布式深度學習的新開源平臺來改變這一狀況&#xff0c;該深度學習…

pytorch梯度下降函數_Pytorch中常用的四種優化器SGD、Momentum、RMSProp、Adam

來源&#xff1a;AINLPer微信公眾號編輯: ShuYini校稿: ShuYini時間: 2019-8-16 引言很多人在使用pytorch的時候都會遇到優化器選擇的問題&#xff0c;今天就給大家介紹對比一下pytorch中常用的四種優化器。SGD、Momentum、RMSProp、Adam。隨機梯度下降法&#xff08;SGD&#…

2019/02/11-分布式數據庫概述

分布式數據庫類型&#xff08;1&#xff09;同構同質型&#xff1a;各場地都是同一種類型的數據庫&#xff0c;如都是關系型數據庫&#xff0c;且都是同一型號的數據庫管理系統&#xff08;2&#xff09;同構異質型&#xff1a;各場地是同一種類型的數據庫&#xff0c;但是數據…

python計算無窮級數求和常用公式_傅里葉變換(二) 從傅里葉級數到傅里葉變換...

在上一部分當中&#xff0c;得到了利用三角函數表示周期函數的方法&#xff0c;但是對于非周期函數就...涼了。所以有什么辦法嗎&#xff1f;沒辦法&#xff08;劃掉&#xff09;。這時候我們就需要拿出來我們的黑科技——傅里葉變換。一、傅里葉級數的推廣當然這東西肯定不是憑…

中鳴投籃機器人怎么組裝_1000余人參加洛陽市青少年機器人競賽

機器人智能識別地面上的黑色線條&#xff0c;并沿著線條來到指定位置&#xff0c;放下“快遞包裹”&#xff1b;無人機在空中飛舞&#xff0c;時而鉆過圓環&#xff0c;時而來個空翻&#xff0c;猶如跳芭蕾般在空中劃過一道優美曲線&#xff1b;橘紅色乒乓球從筒道中送出&#…

Exchange隊列優先級介紹和配置

一、場景 在日常辦公環境中所有郵件都會存在重要與非重要的情況&#xff0c;并且不同的郵箱的使用人的級別也不一樣&#xff0c;不一樣的職位級別要求不一樣的運維等級&#xff0c;以及發送郵件要求的速度也不一樣。這就導致了郵件需要按照重要性進行分類&#xff0c;重要的郵件…

Mybatis源碼閱讀(一):Mybatis初始化1.3 —— 解析sql片段和sql節點

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

IBM研究院計畫5年改變人類生活創新預測

IBM研究院近日發布未來5年將會改變人類生活方式的5項創新預測&#xff08;IBM 5 in 5&#xff09;&#xff0c;包含透過數字分身&#xff08;Digital Twin&#xff09;農業將用更少的資源供給不斷增長的人口、區塊鏈能防范更多的食物浪費、用微生物基因組群保護人類受到有害細菌…

添加請求頭 retrofit_RxJava 與 Retrofit 結合的最佳實踐

前言RxJava和Retrofit也火了一段時間了&#xff0c;不過最近一直在學習ReactNative和Node相關的姿勢&#xff0c;一直沒有時間研究這些新東西&#xff0c;最近有個項目準備寫&#xff0c;打算先用Android寫一個Demo出來&#xff0c;卻發現Android的世界發生了天翻地覆的變化&am…

Mybatis源碼閱讀(二):動態節點解析2.1 —— SqlSource和SqlNode

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

k8s邊緣節點_邊緣計算,如何啃下集群管理這塊硬骨頭?

導讀邊緣計算平臺&#xff0c;旨在將邊緣端靠近數據源的計算單元納入到中心云&#xff0c;實現集中管理&#xff0c;將云服務部署其上&#xff0c;及時響應終端請求。然而&#xff0c;成千上萬的邊緣節點散布于各地&#xff0c;例如銀行網點、車載節點等&#xff0c;節點數量甚…

Mybatis源碼閱讀(二):動態節點解析2.2 —— SqlSourceBuilder與三種SqlSource

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…