在JDK1.5已經提供了Future和Callable的實現,能夠用於阻塞式獲取結果,若是想要異步獲取結果,一般都會以輪詢的方式去獲取結果,以下:編程
//定義一個異步任務 Future<String> future = executor.submit(()->{ Thread.sleep(2000); return "hello world"; }); //輪詢獲取結果 while (true){ if(future.isDone()) { System.out.println(future.get()); break; } }
從上面的形式看來輪詢的方式會耗費無謂的CPU資源,並且也不能及時地獲得計算結果.因此要實現真正的異步,上述這樣是徹底不夠的,在Netty中,咱們隨處可見異步編程app
ChannelFuture f = serverBootstrap.bind(port).sync(); f.addListener(new GenericFutureListener<Future<? super Void>>() { @Override public void operationComplete(Future<? super Void> future) throws Exception { System.out.println("complete"); } });
而JDK1.8中的CompletableFuture
就爲咱們提供了異步函數式編程,CompletableFuture
提供了很是強大的Future
的擴展功能,能夠幫助咱們簡化異步編程的複雜性,提供了函數式編程的能力,能夠經過回調的方式處理計算結果,而且提供了轉換和組合CompletableFuture
的方法。dom
CompletableFuture
提供了四個靜態方法用來建立CompletableFuture對象:異步
public static CompletableFuture<Void> runAsync(Runnable runnable) public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
Asynsc
表示異步,而supplyAsync
與runAsync
不一樣在與前者異步返回一個結果,後者是void.第二個函數第二個參數表示是用咱們本身建立的線程池,不然採用默認的ForkJoinPool.commonPool()
做爲它的線程池.其中Supplier
是一個函數式接口,表明是一個生成者的意思,傳入0個參數,返回一個結果.(更詳細的能夠看我另外一篇文章)ide
CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{ return "hello world"; }); System.out.println(future.get()); //阻塞的獲取結果 ''helllo world"
如下4個方法用於獲取結果函數式編程
//同步獲取結果 public T get() public T get(long timeout, TimeUnit unit) public T getNow(T valueIfAbsent) public T join()
getNow
有點特殊,若是結果已經計算完則返回結果或者拋出異常,不然返回給定的valueIfAbsent值。join()
與get()
區別在於join()
返回計算的結果或者拋出一個unchecked異常(CompletionException),而get()
返回一個具體的異常.異步編程
public boolean complete(T value) public boolean completeExceptionally(Throwable ex)
上面方法表示當調用CompletableFuture.get()
被阻塞的時候,那麼這個方法就是結束阻塞,而且get()
獲取設置的value.函數
public static CompletableFuture<Integer> compute() { final CompletableFuture<Integer> future = new CompletableFuture<>(); return future; } public static void main(String[] args) throws Exception { final CompletableFuture<Integer> f = compute(); class Client extends Thread { CompletableFuture<Integer> f; Client(String threadName, CompletableFuture<Integer> f) { super(threadName); this.f = f; } @Override public void run() { try { System.out.println(this.getName() + ": " + f.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } new Client("Client1", f).start(); new Client("Client2", f).start(); System.out.println("waiting"); //設置Future.get()獲取到的值 f.complete(100); //以異常的形式觸發計算 //f.completeExceptionally(new Exception()); Thread.sleep(1000); }
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action) public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action) public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor) public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)
上面4個方法是當計算階段結束的時候觸發,BiConsumer
有兩個入參,分別表明計算返回值,另一個是異常.無返回值.方法不以Async結尾,意味着Action使用相同的線程執行,而Async可能會使用其它的線程去執行(若是使用相同的線程池,也可能會被同一個線程選中執行)。this
future.whenCompleteAsync((v,e)->{ System.out.println("return value:"+v+" exception:"+e); });
public <U> CompletableFuture<U> handle(BiFunction<? super T,Throwable,? extends U> fn) public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn) public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn, Executor executor)
與whenComplete()
不一樣的是這個函數返回CompletableFuture
並非原始的CompletableFuture
返回的值,而是BiFunction
返回的值.spa
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn) public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
它們與handle方法的區別在於handle方法會處理正常計算值和異常,所以它能夠屏蔽異常,避免異常繼續拋出。而thenApply方法只是用來處理正常值,所以一旦有異常就會拋出。
CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{ return "hello world"; }); CompletableFuture<String> future3 = future.thenApply((element)->{ return element+" addPart"; }).thenApply((element)->{ return element+" addTwoPart"; }); System.out.println(future3.get());//hello world addPart addTwoPart
只對CompletableFuture
的結果進行消費,無返回值,也就是最後的CompletableFuture
是void.
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)
//入參爲原始的CompletableFuture的結果. CompletableFuture future4 = future.thenAccept((e)->{ System.out.println("without return value"); }); future4.get();
這個方法用來組合兩個CompletableFuture
,其中一個CompletableFuture
等待另外一個CompletableFuture
的結果.
CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{ return "hello world"; }); CompletableFuture future5 = future.thenAcceptBoth(CompletableFuture.completedFuture("compose"), (x, y) -> System.out.println(x+y));//hello world compose
thenAcceptBoth
是當兩個CompletableFuture
都計算完成,而咱們下面要了解的方法applyToEither
是當任意一個CompletableFuture計算完成的時候就會執行。
Random rand = new Random(); CompletableFuture<Integer> future9 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000 + rand.nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } return 100; }); CompletableFuture<Integer> future10 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000 + rand.nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } return 200; }); //兩個中任意一個計算完成,那麼觸發Runnable的執行 CompletableFuture<String> f = future10.applyToEither(future9,i -> i.toString()); //兩個都計算完成,那麼觸發Runnable的執行 CompletableFuture f1 = future10.acceptEither(future9,(e)->{ System.out.println(e); }); System.out.println(f.get());
若是想組合超過2個以上的CompletableFuture
,allOf
和anyOf
可能會知足你的要求.allOf
方法是當全部的CompletableFuture
都執行完後執行計算。anyOf
方法是當任意一個CompletableFuture
執行完後就會執行計算,計算的結果相同。
有了CompletableFuture
以後,咱們本身實現異步編程變得輕鬆不少,這個類也提供了許多方法來組合CompletableFuture
.結合Lambada表達式來用,變得很輕鬆.