Java?8?的異步編程利器?CompletableFuture的實(shí)例詳解
最近剛好使用CompeletableFuture優(yōu)化了項(xiàng)目中的代碼,所以跟大家一起學(xué)習(xí)CompletableFuture
一個(gè)例子回顧 Future
因?yàn)镃ompletableFuture實(shí)現(xiàn)了Future
接口,我們先來(lái)回顧Future吧
Future是Java5新加的一個(gè)接口,它提供了一種異步并行計(jì)算的功能。如果主線程需要執(zhí)行一個(gè)很耗時(shí)的計(jì)算任務(wù),我們就可以通過(guò)future把這個(gè)任務(wù)放到異步線程中執(zhí)行。主線程繼續(xù)處理其他任務(wù),異步線程處理完成后,再通過(guò)Future獲取計(jì)算結(jié)果
來(lái)看個(gè)簡(jiǎn)單例子吧,假設(shè)我們有兩個(gè)任務(wù)服務(wù),一個(gè)查詢用戶基本信息,一個(gè)是查詢用戶勛章信息。如下
public class UserInfoService { public UserInfo getUserInfo(Long userId) throws InterruptedException { Thread.sleep(300);//模擬調(diào)用耗時(shí) return new UserInfo("666", "技術(shù)人成長(zhǎng)之路", 27); //一般是查數(shù)據(jù)庫(kù),或者遠(yuǎn)程調(diào)用返回的 } } public class MedalService { public MedalInfo getMedalInfo(long userId) throws InterruptedException { Thread.sleep(500); //模擬調(diào)用耗時(shí) return new MedalInfo("666", "守護(hù)勛章");
接下來(lái),我們來(lái)演示下,在主線程中是如何使用Future來(lái)進(jìn)行異步調(diào)用的。
public class FutureTest { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(10); UserInfoService userInfoService = new UserInfoService(); MedalService medalService = new MedalService(); long userId =666L; long startTime = System.currentTimeMillis(); //調(diào)用用戶服務(wù)獲取用戶基本信息 FutureTask<UserInfo> userInfoFutureTask = new FutureTask<>(new Callable<UserInfo>() { @Override public UserInfo call() throws Exception { return userInfoService.getUserInfo(userId); } }); executorService.submit(userInfoFutureTask); Thread.sleep(300); //模擬主線程其它操作耗時(shí) FutureTask<MedalInfo> medalInfoFutureTask = new FutureTask<>(new Callable<MedalInfo>() { public MedalInfo call() throws Exception { return medalService.getMedalInfo(userId); executorService.submit(medalInfoFutureTask); UserInfo userInfo = userInfoFutureTask.get();//獲取個(gè)人信息結(jié)果 MedalInfo medalInfo = medalInfoFutureTask.get();//獲取勛章信息結(jié)果 System.out.println("總共用時(shí)" + (System.currentTimeMillis() - startTime) + "ms"); } }
運(yùn)行結(jié)果:
總共用時(shí)806ms
如果我們不使用Future進(jìn)行并行異步調(diào)用,而是在主線程串行進(jìn)行的話,耗時(shí)大約為300+500+300 = 1100 ms??梢园l(fā)現(xiàn),future+線程池異步配合,提高了程序的執(zhí)行效率。
但是Future對(duì)于結(jié)果的獲取,不是很友好,只能通過(guò)阻塞或者輪詢的方式得到任務(wù)的結(jié)果。
- Future.get() 就是阻塞調(diào)用,在線程獲取結(jié)果之前get方法會(huì)一直阻塞。
- Future提供了一個(gè)isDone方法,可以在程序中輪詢這個(gè)方法查詢執(zhí)行結(jié)果。
阻塞的方式和異步編程的設(shè)計(jì)理念相違背,而輪詢的方式會(huì)耗費(fèi)無(wú)謂的CPU資源。因此,JDK8設(shè)計(jì)出CompletableFuture。CompletableFuture提供了一種觀察者模式類似的機(jī)制,可以讓任務(wù)執(zhí)行完成后通知監(jiān)聽(tīng)的一方。
一個(gè)例子走進(jìn)CompletableFuture
我們還是基于以上Future的例子,改用CompletableFuture 來(lái)實(shí)現(xiàn)
public class FutureTest { public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException { UserInfoService userInfoService = new UserInfoService(); MedalService medalService = new MedalService(); long userId =666L; long startTime = System.currentTimeMillis(); //調(diào)用用戶服務(wù)獲取用戶基本信息 CompletableFuture<UserInfo> completableUserInfoFuture = CompletableFuture.supplyAsync(() -> userInfoService.getUserInfo(userId)); Thread.sleep(300); //模擬主線程其它操作耗時(shí) CompletableFuture<MedalInfo> completableMedalInfoFuture = CompletableFuture.supplyAsync(() -> medalService.getMedalInfo(userId)); UserInfo userInfo = completableUserInfoFuture.get(2,TimeUnit.SECONDS);//獲取個(gè)人信息結(jié)果 MedalInfo medalInfo = completableMedalInfoFuture.get();//獲取勛章信息結(jié)果 System.out.println("總共用時(shí)" + (System.currentTimeMillis() - startTime) + "ms"); } }
可以發(fā)現(xiàn),使用CompletableFuture,代碼簡(jiǎn)潔了很多。CompletableFuture的supplyAsync方法,提供了異步執(zhí)行的功能,線程池也不用單獨(dú)創(chuàng)建了。實(shí)際上,它CompletableFuture使用了默認(rèn)線程池是ForkJoinPool.commonPool。
CompletableFuture提供了幾十種方法,輔助我們的異步任務(wù)場(chǎng)景。這些方法包括創(chuàng)建異步任務(wù)、任務(wù)異步回調(diào)、多個(gè)任務(wù)組合處理等方面。我們一起來(lái)學(xué)習(xí)吧
CompletableFuture使用場(chǎng)景
創(chuàng)建異步任務(wù)
CompletableFuture創(chuàng)建異步任務(wù),一般有supplyAsync和runAsync兩個(gè)方法
創(chuàng)建異步任務(wù)
- supplyAsync執(zhí)行CompletableFuture任務(wù),支持返回值
- runAsync執(zhí)行CompletableFuture任務(wù),沒(méi)有返回值。
supplyAsync方法
//使用默認(rèn)內(nèi)置線程池ForkJoinPool.commonPool(),根據(jù)supplier構(gòu)建執(zhí)行任務(wù) public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) //自定義線程,根據(jù)supplier構(gòu)建執(zhí)行任務(wù) public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
runAsync方法
//使用默認(rèn)內(nèi)置線程池ForkJoinPool.commonPool(),根據(jù)runnable構(gòu)建執(zhí)行任務(wù) public static CompletableFuture<Void> runAsync(Runnable runnable) //自定義線程,根據(jù)runnable構(gòu)建執(zhí)行任務(wù) public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
實(shí)例代碼如下:
public class FutureTest { public static void main(String[] args) { //可以自定義線程池 ExecutorService executor = Executors.newCachedThreadPool(); //runAsync的使用 CompletableFuture<Void> runFuture = CompletableFuture.runAsync(() -> System.out.println("run,技術(shù)人成長(zhǎng)之路"), executor); //supplyAsync的使用 CompletableFuture<String> supplyFuture = CompletableFuture.supplyAsync(() -> { System.out.print("supply,技術(shù)人成長(zhǎng)之路"); return "技術(shù)人成長(zhǎng)之路"; }, executor); //runAsync的future沒(méi)有返回值,輸出null System.out.println(runFuture.join()); //supplyAsync的future,有返回值 System.out.println(supplyFuture.join()); executor.shutdown(); // 線程池需要關(guān)閉 } } //輸出 run,技術(shù)人成長(zhǎng)之路 null supply,技術(shù)人成長(zhǎng)之路技術(shù)人成長(zhǎng)之路
任務(wù)異步回調(diào)
1. thenRun/thenRunAsync
public CompletableFuture<Void> thenRun(Runnable action); public CompletableFuture<Void> thenRunAsync(Runnable action);
CompletableFuture的thenRun方法,通俗點(diǎn)講就是,做完第一個(gè)任務(wù)后,再做第二個(gè)任務(wù)。某個(gè)任務(wù)執(zhí)行完成后,執(zhí)行回調(diào)方法;但是前后兩個(gè)任務(wù)沒(méi)有參數(shù)傳遞,第二個(gè)任務(wù)也沒(méi)有返回值
public class FutureThenRunTest { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> orgFuture = CompletableFuture.supplyAsync( ()->{ System.out.println("先執(zhí)行第一個(gè)CompletableFuture方法任務(wù)"); return "技術(shù)人成長(zhǎng)之路"; } ); CompletableFuture thenRunFuture = orgFuture.thenRun(() -> { System.out.println("接著執(zhí)行第二個(gè)任務(wù)"); }); System.out.println(thenRunFuture.get()); } } //輸出 先執(zhí)行第一個(gè)CompletableFuture方法任務(wù) 接著執(zhí)行第二個(gè)任務(wù) null
thenRun 和 thenRunAsync 有什么區(qū)別呢?可以看下源碼哈:
private static final Executor asyncPool = useCommonPool ? ForkJoinPool.commonPool() : new ThreadPerTaskExecutor(); public CompletableFuture<Void> thenRun(Runnable action) { return uniRunStage(null, action); } public CompletableFuture<Void> thenRunAsync(Runnable action) { return uniRunStage(asyncPool, action); }
如果你執(zhí)行第一個(gè)任務(wù)的時(shí)候,傳入了一個(gè)自定義線程池:
- 調(diào)用thenRun方法執(zhí)行第二個(gè)任務(wù)時(shí),則第二個(gè)任務(wù)和第一個(gè)任務(wù)是共用同一個(gè)線程池。
- 調(diào)用thenRunAsync方法執(zhí)行第二個(gè)任務(wù)時(shí),則第一個(gè)任務(wù)使用的是你自己傳入的線程池,第二個(gè)任務(wù)使用的是ForkJoin線程池
TIPS:后面介紹的thenAccept和thenAcceptAsync,thenApply和thenApplyAsync等,它們之間的區(qū)別也是這個(gè)哈。
2.thenAccept/thenAcceptAsync
CompletableFuture的thenAccept方法表示,第一個(gè)任務(wù)執(zhí)行完成后,執(zhí)行第二個(gè)回調(diào)方法任務(wù),會(huì)將第一個(gè)任務(wù)的執(zhí)行結(jié)果,作為第二個(gè)任務(wù)的入?yún)?,傳遞到回調(diào)方法中,但是回調(diào)方法是沒(méi)有返回值的。
public class FutureThenAcceptTest { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> orgFuture = CompletableFuture.supplyAsync( ()->{ System.out.println("原始CompletableFuture方法任務(wù)"); return "技術(shù)人成長(zhǎng)之路"; } ); CompletableFuture thenAcceptFuture = orgFuture.thenAccept((a) -> { if ("技術(shù)人成長(zhǎng)之路".equals(a)) { System.out.println("關(guān)注了"); } System.out.println("先考慮考慮"); }); System.out.println(thenAcceptFuture.get()); } }
3. thenApply/thenApplyAsync
CompletableFuture的thenApply方法表示,第一個(gè)任務(wù)執(zhí)行完成后,執(zhí)行第二個(gè)回調(diào)方法任務(wù),會(huì)將第一個(gè)任務(wù)的執(zhí)行結(jié)果,作為第二個(gè)任務(wù)的入?yún)?,傳遞到回調(diào)方法中,并且回調(diào)方法是有返回值的。
public class FutureThenApplyTest { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> orgFuture = CompletableFuture.supplyAsync( ()->{ System.out.println("原始CompletableFuture方法任務(wù)"); return "技術(shù)人成長(zhǎng)之路"; } ); CompletableFuture<String> thenApplyFuture = orgFuture.thenApply((a) -> { if ("技術(shù)人成長(zhǎng)之路".equals(a)) { return "關(guān)注了"; } return "先考慮考慮"; }); System.out.println(thenApplyFuture.get()); } } //輸出 原始CompletableFuture方法任務(wù) 關(guān)注了
4. exceptionally
CompletableFuture的exceptionally方法表示,某個(gè)任務(wù)執(zhí)行異常時(shí),執(zhí)行的回調(diào)方法;并且有拋出異常作為參數(shù),傳遞到回調(diào)方法。
public class FutureExceptionTest { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> orgFuture = CompletableFuture.supplyAsync( ()->{ System.out.println("當(dāng)前線程名稱:" + Thread.currentThread().getName()); throw new RuntimeException(); } ); CompletableFuture<String> exceptionFuture = orgFuture.exceptionally((e) -> { e.printStackTrace(); return "你的程序異常啦"; }); System.out.println(exceptionFuture.get()); } } //輸出 當(dāng)前線程名稱:ForkJoinPool.commonPool-worker-1 java.util.concurrent.CompletionException: java.lang.RuntimeException at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273) at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280) at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1592) at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1582) at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056) at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692) at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157) Caused by: java.lang.RuntimeException at cn.eovie.future.FutureWhenTest.lambda$main$0(FutureWhenTest.java:13) at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590) ... 5 more 你的程序異常啦
5. whenComplete方法
CompletableFuture的whenComplete方法表示,某個(gè)任務(wù)執(zhí)行完成后,執(zhí)行的回調(diào)方法,無(wú)返回值;并且whenComplete方法返回的CompletableFuture的result是上個(gè)任務(wù)的結(jié)果。
public class FutureWhenTest { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> orgFuture = CompletableFuture.supplyAsync( ()->{ System.out.println("當(dāng)前線程名稱:" + Thread.currentThread().getName()); try { Thread.sleep(2000L); } catch (InterruptedException e) { e.printStackTrace(); } return "技術(shù)人成長(zhǎng)之路"; } ); CompletableFuture<String> rstFuture = orgFuture.whenComplete((a, throwable) -> { System.out.println("當(dāng)前線程名稱:" + Thread.currentThread().getName()); System.out.println("上個(gè)任務(wù)執(zhí)行完啦,還把" + a + "傳過(guò)來(lái)"); if ("技術(shù)人成長(zhǎng)之路".equals(a)) { System.out.println("666"); } System.out.println("233333"); }); System.out.println(rstFuture.get()); } } //輸出 當(dāng)前線程名稱:ForkJoinPool.commonPool-worker-1 當(dāng)前線程名稱:ForkJoinPool.commonPool-worker-1 上個(gè)任務(wù)執(zhí)行完啦,還把技術(shù)人成長(zhǎng)之路傳過(guò)來(lái) 666 233333 技術(shù)人成長(zhǎng)之路
6. handle方法
CompletableFuture的handle方法表示,某個(gè)任務(wù)執(zhí)行完成后,執(zhí)行回調(diào)方法,并且是有返回值的;并且handle方法返回的CompletableFuture的result是回調(diào)方法執(zhí)行的結(jié)果。
public class FutureHandlerTest { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> orgFuture = CompletableFuture.supplyAsync( ()->{ System.out.println("當(dāng)前線程名稱:" + Thread.currentThread().getName()); try { Thread.sleep(2000L); } catch (InterruptedException e) { e.printStackTrace(); } return "技術(shù)人成長(zhǎng)之路"; } ); CompletableFuture<String> rstFuture = orgFuture.handle((a, throwable) -> { System.out.println("上個(gè)任務(wù)執(zhí)行完啦,還把" + a + "傳過(guò)來(lái)"); if ("技術(shù)人成長(zhǎng)之路".equals(a)) { System.out.println("666"); return "關(guān)注了"; } System.out.println("233333"); return null; }); System.out.println(rstFuture.get()); } } //輸出 當(dāng)前線程名稱:ForkJoinPool.commonPool-worker-1 上個(gè)任務(wù)執(zhí)行完啦,還把技術(shù)人成長(zhǎng)之路傳過(guò)來(lái) 666 關(guān)注了
多個(gè)任務(wù)組合處理
AND組合關(guān)系
thenCombine / thenAcceptBoth / runAfterBoth都表示:將兩個(gè)CompletableFuture組合起來(lái),只有這兩個(gè)都正常執(zhí)行完了,才會(huì)執(zhí)行某個(gè)任務(wù)。
區(qū)別在于:
- thenCombine:會(huì)將兩個(gè)任務(wù)的執(zhí)行結(jié)果作為方法入?yún)?,傳遞到指定方法中,且有返回值
- thenAcceptBoth: 會(huì)將兩個(gè)任務(wù)的執(zhí)行結(jié)果作為方法入?yún)?,傳遞到指定方法中,且無(wú)返回值
- runAfterBoth:不會(huì)把執(zhí)行結(jié)果當(dāng)做方法入?yún)?/strong>,且沒(méi)有返回值。
public class ThenCombineTest { public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException { CompletableFuture<String> first = CompletableFuture.completedFuture("第一個(gè)異步任務(wù)"); ExecutorService executor = Executors.newFixedThreadPool(10); CompletableFuture<String> future = CompletableFuture //第二個(gè)異步任務(wù) .supplyAsync(() -> "第二個(gè)異步任務(wù)", executor) // (w, s) -> System.out.println(s) 是第三個(gè)任務(wù) .thenCombineAsync(first, (s, w) -> { System.out.println(w); System.out.println(s); return "兩個(gè)異步任務(wù)的組合"; }, executor); System.out.println(future.join()); executor.shutdown(); } } //輸出 第一個(gè)異步任務(wù) 第二個(gè)異步任務(wù) 兩個(gè)異步任務(wù)的組合
OR組合的關(guān)系
applyToEither / acceptEither / runAfterEither 都表示:將兩個(gè)CompletableFuture組合起來(lái),只要其中一個(gè)執(zhí)行完了,就會(huì)執(zhí)行某個(gè)任務(wù)。
區(qū)別在于:
- applyToEither:會(huì)將已經(jīng)執(zhí)行完成的任務(wù),作為方法入?yún)ⅲ瑐鬟f到指定方法中,且有返回值
- acceptEither: 會(huì)將已經(jīng)執(zhí)行完成的任務(wù),作為方法入?yún)ⅲ瑐鬟f到指定方法中,且無(wú)返回值
- runAfterEither:不會(huì)把執(zhí)行結(jié)果當(dāng)做方法入?yún)ⅲ覜](méi)有返回值。
public class AcceptEitherTest { public static void main(String[] args) { //第一個(gè)異步任務(wù),休眠2秒,保證它執(zhí)行晚點(diǎn) CompletableFuture<String> first = CompletableFuture.supplyAsync(()->{ try{ Thread.sleep(2000L); System.out.println("執(zhí)行完第一個(gè)異步任務(wù)");} catch (Exception e){ return "第一個(gè)任務(wù)異常"; } return "第一個(gè)異步任務(wù)"; }); ExecutorService executor = Executors.newSingleThreadExecutor(); CompletableFuture<Void> future = CompletableFuture //第二個(gè)異步任務(wù) .supplyAsync(() -> { System.out.println("執(zhí)行完第二個(gè)任務(wù)"); return "第二個(gè)任務(wù)";} , executor) //第三個(gè)任務(wù) .acceptEitherAsync(first, System.out::println, executor); executor.shutdown(); } } //輸出 執(zhí)行完第二個(gè)任務(wù) 第二個(gè)任務(wù)
AllOf
所有任務(wù)都執(zhí)行完成后,才執(zhí)行 allOf 返回的CompletableFuture。如果任意一個(gè)任務(wù)異常,allOf的CompletableFuture,執(zhí)行g(shù)et方法,會(huì)拋出異常
public class allOfFutureTest { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Void> a = CompletableFuture.runAsync(()->{ System.out.println("我執(zhí)行完了"); }); CompletableFuture<Void> b = CompletableFuture.runAsync(() -> { System.out.println("我也執(zhí)行完了"); }); CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(a, b).whenComplete((m,k)->{ System.out.println("finish"); }); } } //輸出 我執(zhí)行完了 我也執(zhí)行完了 finish
AnyOf
任意一個(gè)任務(wù)執(zhí)行完,就執(zhí)行anyOf返回的CompletableFuture。如果執(zhí)行的任務(wù)異常,anyOf的CompletableFuture,執(zhí)行g(shù)et方法,會(huì)拋出異常
public class AnyOfFutureTest { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Void> a = CompletableFuture.runAsync(()->{ try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("我執(zhí)行完了"); }); CompletableFuture<Void> b = CompletableFuture.runAsync(() -> { System.out.println("我也執(zhí)行完了"); }); CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(a, b).whenComplete((m,k)->{ System.out.println("finish"); // return "技術(shù)人成長(zhǎng)之路"; }); anyOfFuture.join(); } } //輸出 我也執(zhí)行完了 finish
thenCompose
thenCompose方法會(huì)在某個(gè)任務(wù)執(zhí)行完成后,將該任務(wù)的執(zhí)行結(jié)果,作為方法入?yún)ⅲ?zhí)行指定的方法。該方法會(huì)返回一個(gè)新的CompletableFuture實(shí)例
- 如果該CompletableFuture實(shí)例的result不為null,則返回一個(gè)基于該result新的CompletableFuture實(shí)例;
- 如果該CompletableFuture實(shí)例為null,然后就執(zhí)行這個(gè)新任務(wù)
public class ThenComposeTest { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> f = CompletableFuture.completedFuture("第一個(gè)任務(wù)"); //第二個(gè)異步任務(wù) ExecutorService executor = Executors.newSingleThreadExecutor(); CompletableFuture<String> future = CompletableFuture .supplyAsync(() -> "第二個(gè)任務(wù)", executor) .thenComposeAsync(data -> { System.out.println(data); return f; //使用第一個(gè)任務(wù)作為返回 }, executor); System.out.println(future.join()); executor.shutdown(); } } //輸出 第二個(gè)任務(wù) 第一個(gè)任務(wù)
CompletableFuture使用有哪些注意點(diǎn)
CompletableFuture 使我們的異步編程更加便利的、代碼更加優(yōu)雅的同時(shí),我們也要關(guān)注下它,使用的一些注意點(diǎn)。
1. Future需要獲取返回值,才能獲取異常信息
ExecutorService executorService = new ThreadPoolExecutor(5, 10, 5L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10)); CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> { int a = 0; int b = 666; int c = b / a; return true; },executorService).thenAccept(System.out::println); //如果不加 get()方法這一行,看不到異常信息 //future.get();
Future需要獲取返回值,才能獲取到異常信息。如果不加 get()/join()方法,看不到異常信息。小伙伴們使用的時(shí)候,注意一下哈,考慮是否加try…catch…或者使用exceptionally方法。
2. CompletableFuture的get()方法是阻塞的
CompletableFuture的get()方法是阻塞的,如果使用它來(lái)獲取異步調(diào)用的返回值,需要添加超時(shí)時(shí)間~
//反例 CompletableFuture.get(); //正例 CompletableFuture.get(5, TimeUnit.SECONDS);
3. 默認(rèn)線程池的注意點(diǎn)
CompletableFuture代碼中又使用了默認(rèn)的線程池,處理的線程個(gè)數(shù)是電腦CPU核數(shù)-1。在大量請(qǐng)求過(guò)來(lái)的時(shí)候,處理邏輯復(fù)雜的話,響應(yīng)會(huì)很慢。一般建議使用自定義線程池,優(yōu)化線程池配置參數(shù)。
4. 自定義線程池時(shí),注意飽和策略
CompletableFuture的get()方法是阻塞的,我們一般建議使用future.get(3, TimeUnit.SECONDS)
。并且一般建議使用自定義線程池。
但是如果線程池拒絕策略是DiscardPolicy
或者DiscardOldestPolicy
,當(dāng)線程池飽和時(shí),會(huì)直接丟棄任務(wù),不會(huì)拋棄異常。因此建議,CompletableFuture線程池策略最好使用AbortPolicy,然后耗時(shí)的異步線程,做好線程池隔離哈。
到此這篇關(guān)于Java 8 的異步編程利器 CompletableFuture 詳解的文章就介紹到這了,更多相關(guān)Java 8 CompletableFuture異步編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java計(jì)算機(jī)器人的運(yùn)動(dòng)范圍
這篇文章主要為大家詳細(xì)介紹了java計(jì)算機(jī)器人運(yùn)動(dòng)范圍的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01Java使用JavaMail API發(fā)送和接收郵件的代碼示例
JavaMail是Oracle甲骨文開(kāi)發(fā)的Java郵件類API,支持多種郵件協(xié)議,這里我們就來(lái)看一下Java使用JavaMail API發(fā)送和接收郵件的代碼示例2016-06-06Delegate IDE build/run actions to maven 配置會(huì)影響程序運(yùn)行嗎?
這篇文章主要介紹了Delegate IDE build/run actions to maven 配置會(huì)影響程序運(yùn)行嗎,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08springboot使用Mybatis-plus分頁(yè)插件的案例詳解
這篇文章主要介紹了springboot使用Mybatis-plus分頁(yè)插件的相關(guān)知識(shí),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05Java實(shí)現(xiàn)添加條形碼到PDF表格的方法詳解
條碼的應(yīng)用已深入生活和工作的方方面面。本文以操作PDF文件為例,介紹如何利用Java語(yǔ)言在編輯表格時(shí),向單元格中添加條形碼,感興趣的可以學(xué)習(xí)一下2022-06-06SpringBoot實(shí)戰(zhàn)項(xiàng)目之谷歌瀏覽器全屏效果實(shí)現(xiàn)
這篇文章主要介紹了通過(guò) Java SpringBoot來(lái)實(shí)現(xiàn)谷歌瀏覽器的全屏效果,希望頁(yè)面展示時(shí)可以實(shí)現(xiàn)全屏效果以提高用戶體驗(yàn)。感興趣的小伙伴跟著小編往下看吧2021-09-09IDEA POM文件配置profile實(shí)現(xiàn)不同環(huán)境切換的方法步驟
這篇文章主要介紹了IDEA POM文件配置profile實(shí)現(xiàn)不同環(huán)境切換的方法步驟2024-03-03