Java并發(fā)編程中的CompletableFuture使用詳解
引言
并行,并發(fā)
并發(fā):一個實體上,多個任務(wù)有序執(zhí)行
并行:多個實體上,多個任務(wù)同時執(zhí)行
用戶線程
用戶線程是系統(tǒng)的工作線程,會完成程序需要完成的業(yè)務(wù)操作
守護(hù)線程
是一種特殊的線程,為其他線程服務(wù)的,在后臺默默的完成一些系統(tǒng)性的服務(wù),如GC線程;
如果用戶線程全部結(jié)束,意味著程序需要完成的業(yè)務(wù)操作已經(jīng)結(jié)束了,守護(hù)線程就沒有必要繼續(xù)運(yùn)行了。所以當(dāng)系統(tǒng)只剩下守護(hù)線程的時候,java虛擬機(jī)會自動退出。
1、Future
Future接口(實現(xiàn)類FutureTask)定義了操作異步任務(wù)執(zhí)行的一些方法,如獲取異步任務(wù)執(zhí)行的結(jié)果、取消任務(wù)的執(zhí)行、判斷任務(wù)是否被取消,判斷任務(wù)是否執(zhí)行完畢等。
Future接口可以為主線程開一個分支線程,專門為主線程處理耗時和費力的復(fù)雜業(yè)務(wù)
Future接口方法
取消任務(wù):
boolean cancel(boolean mayInterruptIfRunning);
判斷任務(wù)是否被取消
boolean isCancelled();
斷任務(wù)是否執(zhí)行完畢
boolean isDone();
獲取異步任務(wù)執(zhí)行的結(jié)果:
V get() throws InterruptedException, ExecutionException;
獲取異步任務(wù)執(zhí)行的結(jié)果(限定時間內(nèi)沒獲取到結(jié)果就拋出異常):
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
注:get方法獲取執(zhí)行結(jié)果會出現(xiàn)程序阻塞,所以一般放到程序最后調(diào)用
示例代碼
//固定大小線程池 ExecutorService threadPool = Executors.newFixedThreadPool(3); FutureTask<String> futureTask1 = new FutureTask<>(()->{ TimeUnit.MICROSECONDS.sleep(500); return "callable1"; }); threadPool.submit(futureTask1); FutureTask<String> futureTask2 = new FutureTask<>(()->{ TimeUnit.SECONDS.sleep(3); return "callable2"; }); threadPool.submit(futureTask2); //獲取異步任務(wù)執(zhí)行結(jié)果 while (true){ if (futureTask1.isDone()){ System.out.println(futureTask1.get()); break; } } System.out.println(futureTask2.get(2,TimeUnit.SECONDS)); //關(guān)閉線程池 threadPool.shutdown();
2、CompletableFuture
CompletableFuture提供了Future的擴(kuò)展功能,提供了函數(shù)式編程能力,可以在任務(wù)執(zhí)行完后通過回調(diào)的方式處理計算結(jié)果。
CompletableFuture的創(chuàng)建
方法:
- runAsync 無返回值
- supplyAsync 有返回值
ExecutorService threadPool = Executors.newFixedThreadPool(3); //無返回值 CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { System.out.println(Thread.currentThread().getName()); try { TimeUnit.MICROSECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } }, threadPool);//不指定線程池就會使用默認(rèn)的線程池 System.out.println(future.get());//null //有返回值 CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> { System.out.println(Thread.currentThread().getName()); try { TimeUnit.MICROSECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } return "supplyAsync"; }, threadPool); System.out.println(future1.get()); threadPool.shutdown();
CompletableFuture示例
一個階段的完成可能會觸發(fā)另一個階段
public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService threadPool = Executors.newFixedThreadPool(3); CompletableFuture.supplyAsync(() -> { int nextInt = new Random().nextInt(10); // int a = 10 /0; return nextInt; }, threadPool).whenComplete((v,e)->{//獲得上一步執(zhí)行完返回的結(jié)果 v;可能出現(xiàn)的異常 e if (e==null){ System.out.println("成功獲得結(jié)果:"+v); } }).exceptionally(e->{//發(fā)生異常后自動調(diào)用 e.printStackTrace(); System.out.println("發(fā)生異常:"+e.getMessage()); return null; }); threadPool.shutdown(); //todo 主線程執(zhí)行任務(wù) (注意:主線程結(jié)束后默認(rèn)線程池會自動關(guān)閉,推薦使用自定義線程池) }
CompletableFuture常用方法
getNow和complete
//getNow:立即獲取結(jié)果,若沒獲取到就使用備選結(jié)果 System.out.println(future1.getNow("xxxxx")); //complete:如果操作未執(zhí)行完就將get獲得的值改為給定的值,然后返回true,反之get獲得的值就是操作執(zhí)行完返回的值,然后返回false System.out.println(future1.complete("beixuan") + "\t" + future1.get());
thenApply:計算結(jié)果存在傳遞關(guān)系,發(fā)生異常時后面步驟停止運(yùn)行
CompletableFuture.supplyAsync(() -> { return 1; }).thenApply(v -> {//當(dāng)這一步發(fā)生異常時,后續(xù)操作不執(zhí)行,直接跳到最后打印異常信息 //int i = 10/0; return v + 2; }).thenApply(v -> { return v + 3; }).whenComplete((v,e)->{ if (e==null){ System.out.println("thenApply:"+v);//6 } }).exceptionally(e->{ e.printStackTrace(); return null; });
handle:和thenApply類似,但發(fā)生異常時后續(xù)操作可以正常執(zhí)行
CompletableFuture.supplyAsync(() -> { System.out.println(1); return 1; }).handle((v,e) -> {//第一步發(fā)生異常停止運(yùn)行,但后面可以正常運(yùn)行,直至最后把異常打印出來 int i = 10/0; System.out.println(3); return v + 2; }).handle((v,e) -> {//這里正常輸出 System.out.println(6); return v + 3; }).whenComplete((v,e)->{ if (e==null){ System.out.println("handle:"+v); } }).exceptionally(e->{ e.printStackTrace(); return null; });
thenAccept:接收上一步執(zhí)行完的結(jié)果,沒有返回值
CompletableFuture.supplyAsync(() -> { return 1; }).thenApply(v -> { return v + 2; }).thenAccept(v -> { System.out.println(v); });
thenCombine:對兩個異步操作的結(jié)果進(jìn)行合并,先完成的操作要等另一個慢的操作
CompletableFuture<Integer> futureA = CompletableFuture.supplyAsync(() -> { try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();} return 10; }); CompletableFuture<Integer> futureB = CompletableFuture.supplyAsync(() -> { try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();} return 20; }); System.out.println(futureA.thenCombine(futureB, (a, b) -> { System.out.println("結(jié)果開始合并"); return a + b; }).join());//30 //======================================================================== System.out.println(CompletableFuture.supplyAsync(() -> { try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();} return 10; }).thenCombine(CompletableFuture.supplyAsync(() -> { try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();} return 20; }), (x, y) -> { System.out.println("結(jié)果開始合并1"); return x + y; }).thenCombine(CompletableFuture.supplyAsync(() -> { try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();} return 30; }), (x, y) -> { System.out.println("結(jié)果開始合并2"); return x + y; }).join());//60
CompletableFuture案例
比較多個商城中同一物品的價格
package com.cheng.juc; import lombok.Data; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; public class GoodsPriceDemo { static List<NetMall> malls = Arrays.asList(new NetMall("JD"),new NetMall("TB"),new NetMall("DD")); /** * 輪流查詢價格 * @param malls * @param goodsName * @return */ public static List<String> getPrice(List<NetMall> malls,String goodsName){ return malls.stream() .map(m -> String.format(goodsName + " in %s price is %.2f", m.getNetMallName(), m.calcPrice(goodsName))) .collect(Collectors.toList()); } /** * 使用異步任務(wù)查詢價格 * @param malls * @param goodsName * @return */ public static List<String> getPricePlus(List<NetMall> malls,String goodsName){ ExecutorService threadPool = Executors.newFixedThreadPool(3); return malls.stream() //為每一個商城開啟一個異步任務(wù),然后同時查詢價格 .map(m-> CompletableFuture.supplyAsync(()-> String.format(goodsName + " in %s price is %.2f", m.getNetMallName(), m.calcPrice(goodsName)),threadPool)) .collect(Collectors.toList()) .stream().map(d->d.join()) .collect(Collectors.toList()); } public static void main(String[] args) { long l1 = System.currentTimeMillis(); // List<String> price = getPrice(malls,"mysql");// 3s List<String> price = getPricePlus(malls,"mysql");// 1s price.stream().forEach(System.out::println); long l2 = System.currentTimeMillis(); System.out.println("耗時:"+(l2 - l1)); } } @Data class NetMall{ private String netMallName; public NetMall(String netMallName){ this.netMallName = netMallName; } //計算價格 public BigDecimal calcPrice(String goodsName){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } BigDecimal result = BigDecimal.valueOf(ThreadLocalRandom.current().nextDouble() * 2 + goodsName.charAt(0)); return result; } }
比較結(jié)果:
mysql in JD price is 110.37
mysql in TB price is 110.58
mysql in DD price is 109.48
到此這篇關(guān)于Java并發(fā)編程中的CompletableFuture使用詳解的文章就介紹到這了,更多相關(guān)Java的CompletableFuture內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于SpringBoot整合SSMP案例(開啟日志與分頁查詢條件查詢功能實現(xiàn))
這篇文章主要介紹了基于SpringBoot整合SSMP案例(開啟日志與分頁查詢條件查詢功能實現(xiàn)),本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋參考下吧2023-11-11struts2+spring+ibatis框架整合實現(xiàn)增刪改查
這篇文章主要為大家詳細(xì)介紹了struts2+spring+ibatis框架整合實現(xiàn)增刪改查操作,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-07-07elasticsearch索引index數(shù)據(jù)功能源碼示例
這篇文章主要為大家介紹了elasticsearch索引index功能源碼示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04SpringCloud Ribbon與OpenFeign詳解如何實現(xiàn)服務(wù)調(diào)用
這篇文章主要介紹了SpringCloud Ribbon與OpenFeign實現(xiàn)服務(wù)調(diào)用的過程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-09-09Spring-AOP 靜態(tài)正則表達(dá)式方法如何匹配切面
這篇文章主要介紹了Spring-AOP 靜態(tài)正則表達(dá)式方法如何匹配切面的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07Java中LinkedHashSet的實現(xiàn)原理詳解
這篇文章主要介紹了Java中LinkedHasSet的實現(xiàn)原理詳解,LinkedHashSet?是具有可預(yù)知迭代順序的?Set?接口的哈希表和鏈接列表實現(xiàn),此實現(xiàn)與HashSet?的不同之處在于,后者維護(hù)著一個運(yùn)行于所有條目的雙重鏈接列表,需要的朋友可以參考下2023-09-09