JDK8新特性之CompletableFuture詳解

????在JDK1.5已經(jīng)提供了Future和Callable的實(shí)現(xiàn),可以用于阻塞式獲取結(jié)果,如果想要異步獲取結(jié)果,通常都會(huì)以輪詢的方式去獲取結(jié)果,如下:

//定義一個(gè)異步任務(wù)
Future<String> future = executor.submit(()->{
       Thread.sleep(2000);
       return "hello world";
});
//輪詢獲取結(jié)果
while (true){
    if(future.isDone()) {
         System.out.println(future.get());
         break;
     }
 }

????從上面的形式看來(lái)輪詢的方式會(huì)耗費(fèi)無(wú)謂的CPU資源,而且也不能及時(shí)地得到計(jì)算結(jié)果.所以要實(shí)現(xiàn)真正的異步,上述這樣是完全不夠的,在Netty中,我們隨處可見(jiàn)異步編程

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就為我們提供了異步函數(shù)式編程,CompletableFuture提供了非常強(qiáng)大的Future的擴(kuò)展功能,可以幫助我們簡(jiǎn)化異步編程的復(fù)雜性绢涡,提供了函數(shù)式編程的能力抡蛙,可以通過(guò)回調(diào)的方式處理計(jì)算結(jié)果妇智,并且提供了轉(zhuǎn)換和組合CompletableFuture的方法齐蔽。

1. 創(chuàng)建CompletableFuture對(duì)象

????CompletableFuture提供了四個(gè)靜態(tài)方法用來(lái)創(chuàng)建CompletableFuture對(duì)象:

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表示異步,而supplyAsyncrunAsync不同在與前者異步返回一個(gè)結(jié)果,后者是void.第二個(gè)函數(shù)第二個(gè)參數(shù)表示是用我們自己創(chuàng)建的線程池,否則采用默認(rèn)的ForkJoinPool.commonPool()作為它的線程池.其中Supplier是一個(gè)函數(shù)式接口,代表是一個(gè)生成者的意思,傳入0個(gè)參數(shù),返回一個(gè)結(jié)果.(更詳細(xì)的可以看我另一篇文章)

CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
            return "hello world";
  });
System.out.println(future.get());  //阻塞的獲取結(jié)果  ''helllo world"

2. 主動(dòng)計(jì)算

????以下4個(gè)方法用于獲取結(jié)果

//同步獲取結(jié)果
public T    get()
public T    get(long timeout, TimeUnit unit)
public T    getNow(T valueIfAbsent)
public T    join()

getNow有點(diǎn)特殊杠巡,如果結(jié)果已經(jīng)計(jì)算完則返回結(jié)果或者拋出異常剑勾,否則返回給定的valueIfAbsent值埃撵。join()get()區(qū)別在于join()返回計(jì)算的結(jié)果或者拋出一個(gè)unchecked異常(CompletionException),而get()返回一個(gè)具體的異常.

  • 主動(dòng)觸發(fā)計(jì)算.
public boolean complete(T  value)
public boolean completeExceptionally(Throwable ex)

上面方法表示當(dāng)調(diào)用CompletableFuture.get()被阻塞的時(shí)候,那么這個(gè)方法就是結(jié)束阻塞,并且get()獲取設(shè)置的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");
        //設(shè)置Future.get()獲取到的值
        f.complete(100);
        //以異常的形式觸發(fā)計(jì)算
        //f.completeExceptionally(new Exception());
        Thread.sleep(1000);
    }

3. 計(jì)算結(jié)果完成時(shí)的處理

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個(gè)方法是當(dāng)計(jì)算階段結(jié)束的時(shí)候觸發(fā),BiConsumer有兩個(gè)入?yún)?分別代表計(jì)算返回值,另外一個(gè)是異常.無(wú)返回值.方法不以Async結(jié)尾虽另,意味著Action使用相同的線程執(zhí)行暂刘,而Async可能會(huì)使用其它的線程去執(zhí)行(如果使用相同的線程池,也可能會(huì)被同一個(gè)線程選中執(zhí)行)捂刺。

future.whenCompleteAsync((v,e)->{
       System.out.println("return value:"+v+"  exception:"+e);
 });
  • handle()
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()不同的是這個(gè)函數(shù)返回CompletableFuture并不是原始的CompletableFuture返回的值,而是BiFunction返回的值.

4. CompletableFuture的組合

  • thenApply
    當(dāng)計(jì)算結(jié)算完成之后,后面可以接繼續(xù)一系列的thenApply,來(lái)完成值的轉(zhuǎn)化.
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方法的區(qū)別在于handle方法會(huì)處理正常計(jì)算值和異常谣拣,因此它可以屏蔽異常,避免異常繼續(xù)拋出族展。而thenApply方法只是用來(lái)處理正常值森缠,因此一旦有異常就會(huì)拋出。

  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

5. CompletableFuture的Consumer

只對(duì)CompletableFuture的結(jié)果進(jìn)行消費(fèi),無(wú)返回值,也就是最后的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)
//入?yún)樵嫉腃ompletableFuture的結(jié)果.
CompletableFuture future4 = future.thenAccept((e)->{
            System.out.println("without return value");
        });
future4.get();
  • thenAcceptBoth

這個(gè)方法用來(lái)組合兩個(gè)CompletableFuture,其中一個(gè)CompletableFuture等待另一個(gè)CompletableFuture的結(jié)果.

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

6. Either和ALL

????thenAcceptBoth是當(dāng)兩個(gè)CompletableFuture都計(jì)算完成仪缸,而我們下面要了解的方法applyToEither是當(dāng)任意一個(gè)CompletableFuture計(jì)算完成的時(shí)候就會(huì)執(zhí)行贵涵。

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;
        });
        //兩個(gè)中任意一個(gè)計(jì)算完成,那么觸發(fā)Runnable的執(zhí)行
        CompletableFuture<String> f =  future10.applyToEither(future9,i -> i.toString());
        //兩個(gè)都計(jì)算完成,那么觸發(fā)Runnable的執(zhí)行
        CompletableFuture f1 = future10.acceptEither(future9,(e)->{
            System.out.println(e);
        });
        System.out.println(f.get());

????如果想組合超過(guò)2個(gè)以上的CompletableFuture,allOfanyOf可能會(huì)滿足你的要求.allOf方法是當(dāng)所有的CompletableFuture都執(zhí)行完后執(zhí)行計(jì)算。anyOf方法是當(dāng)任意一個(gè)CompletableFuture執(zhí)行完后就會(huì)執(zhí)行計(jì)算,計(jì)算的結(jié)果相同宾茂。

總結(jié)

????有了CompletableFuture之后,我們自己實(shí)現(xiàn)異步編程變得輕松很多,這個(gè)類(lèi)也提供了許多方法來(lái)組合CompletableFuture.結(jié)合Lambada表達(dá)式來(lái)用,變得很輕松.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瓷马,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子跨晴,更是在濱河造成了極大的恐慌欧聘,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件端盆,死亡現(xiàn)場(chǎng)離奇詭異怀骤,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)爱谁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)晒喷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人访敌,你說(shuō)我怎么就攤上這事凉敲。” “怎么了寺旺?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵爷抓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我阻塑,道長(zhǎng)蓝撇,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任陈莽,我火速辦了婚禮渤昌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘走搁。我一直安慰自己独柑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布私植。 她就那樣靜靜地躺著忌栅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪曲稼。 梳的紋絲不亂的頭發(fā)上索绪,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音贫悄,去河邊找鬼瑞驱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛窄坦,可吹牛的內(nèi)容都是我干的钱烟。 我是一名探鬼主播晰筛,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拴袭!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起曙博,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拥刻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后父泳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體般哼,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年惠窄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蒸眠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杆融,死狀恐怖楞卡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情脾歇,我是刑警寧澤蒋腮,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站藕各,受9級(jí)特大地震影響池摧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜激况,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一作彤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乌逐,春花似錦竭讳、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至成黄,卻和暖如春呐芥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奋岁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工思瘟, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闻伶。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓滨攻,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子光绕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容