CompletableFuture 詳解

JAVA8之前的Future

public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Future<String> future = executorService.submit(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "callable finished";
        });
        //do something else
        Thread.sleep(2000);
        String callableResult = future.get();
        System.out.println(callableResult);
    }

CompletableFuture的優(yōu)勢

  1. 提供了異步程序執(zhí)行的另一種方式:回調(diào),不需要像future.get()通過阻塞線程來獲取異步結(jié)果或者通過isDone來檢測異步線程是否完成來執(zhí)行后續(xù)程序。
  2. 能夠管理多個(gè)異步流程,并根據(jù)需要選擇已經(jīng)結(jié)束的異步流程返回結(jié)果。

構(gòu)建CompletableFuture

構(gòu)造函數(shù)

 /**
     * Creates a new incomplete CompletableFuture.
     */
    public CompletableFuture() {
    }

純翻譯:構(gòu)建一個(gè)不完整的CompletableFuture,為什么說不完整呢,請(qǐng)往下看

式例1

public static class test{
        public static String getTestResult()
        {
            int i = 10/0;
            return "test";
        }
    }
    public static void main(String[] args) {

        CompletableFuture<String> completableFuture  = new CompletableFuture();
        new Thread(()->{
            try {
                completableFuture.complete(test.getTestResult());
            } catch (Exception e) {
                System.out.println("get exception in side");
                completableFuture.completeExceptionally(e);
            }
        }).start();

        try {
            String result = completableFuture.get();
            System.out.println(result);
        } catch (Exception e) {
            System.out.println("get exception out side");
            e.printStackTrace();
        }
    }

一般需要complete() 設(shè)置異步線程可能返回的值,以及completeExceptionally() 向上拋出異常

工廠方法

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                       Executor executor)
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                   Executor executor)

從入?yún)⒖梢钥吹剑珻ompletableFuture 允許我們自定義執(zhí)行器,在實(shí)際項(xiàng)目中我們可以選擇合適的線程池來提高異步程序的效率。

    CompletableFuture completableFuture = CompletableFuture.supplyAsync(() ->
                { 
                    try {Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return "test";
                }
        );

CompletableFuture 流

java8 流式處理在CompletableFuture 也得到了完美的體現(xiàn),流處理包含的中間操作,終端操作分別對(duì)應(yīng)CompletableFuture 中以thenAccept開頭返回CompletableFuture <Void>(也就是回調(diào))的實(shí)例方法。中間操作對(duì)應(yīng)thenApply,thenCompose等等返回非CompletableFuture <Void>的實(shí)例方法。

式例2

 CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1);
                System.out.println(Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "test ";
        }).thenApply(u -> {
            System.out.println(Thread.currentThread().getName());
            return u + "in thenApply first";
        })
                .thenCompose(u -> CompletableFuture.supplyAsync(() -> {
                            System.out.println(Thread.currentThread().getName());
                            return u + "in thenCompose second";
                        })
                ).thenAccept(u -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println(u + "in thenAccept last");
        });
ForkJoinPool.commonPool-worker-1
main
ForkJoinPool.commonPool-worker-1
main
test in thenApply firstin thenCompose secondin thenAccept last

可以看到默認(rèn)的異步線程池都是ForkJoinPool.commonPool,同步操作都在main線程中處理。
多說一句thenApply 和thenCompose的區(qū)別,thenCompose在調(diào)用外部接口返回CompletableFuture<>類型時(shí)更方便。

多個(gè)CompletableFuture任務(wù)的管理

現(xiàn)實(shí)應(yīng)用中可能同時(shí)存在多個(gè)異步任務(wù),有時(shí)候我們需要他們一起完成才能進(jìn)行下面的操作,有時(shí)候我們又只需要在存在一個(gè)結(jié)果的情況下就返回。

   private static  final Random random = new Random();
    public static  String randomDelay()
    {
        int delay = 500 + random.nextInt(2000);
        try {
            System.out.println(String.format("%s sleep in %d",Thread.currentThread().getName(),delay));
            Thread.sleep(delay);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(String.format("%s sleep in %s",Thread.currentThread().getName(),"end"));
        return Thread.currentThread().getName()+" return";
    }
    public static void main(String[] args) {
         CompletableFuture [] futures = {CompletableFuture.supplyAsync(()->randomDelay()),
                 CompletableFuture.supplyAsync(()->randomDelay()),CompletableFuture.supplyAsync(()->randomDelay())};
        CompletableFuture.allOf(futures).join();
        System.out.println("all timeout process end");
    }
ForkJoinPool.commonPool-worker-2 sleep in 1957
ForkJoinPool.commonPool-worker-3 sleep in 2097
ForkJoinPool.commonPool-worker-1 sleep in 2422
ForkJoinPool.commonPool-worker-2 sleep in end
ForkJoinPool.commonPool-worker-3 sleep in end
ForkJoinPool.commonPool-worker-1 sleep in end
all timeout process end

上段代碼展示了 CompletableFuture.allOf 的用法,可以看到所有的線程結(jié)束后打印了"all timeout process end",注意 allOf 接受的是數(shù)組類對(duì)象。如果把a(bǔ)llOf改為 anyOf

CompletableFuture [] futures = {CompletableFuture.supplyAsync(()->randomDelay()),
                 CompletableFuture.supplyAsync(()->randomDelay()),CompletableFuture.supplyAsync(()->randomDelay())};
        System.out.println(CompletableFuture.anyOf(futures).get());
        System.out.println("all timeout process end");
ForkJoinPool.commonPool-worker-2 sleep in 529
ForkJoinPool.commonPool-worker-3 sleep in 759
ForkJoinPool.commonPool-worker-1 sleep in 1750
ForkJoinPool.commonPool-worker-2 sleep in end
ForkJoinPool.commonPool-worker-2 return
all timeout process end

可以看到只有一個(gè)線程結(jié)束時(shí)結(jié)果已經(jīng)返回,另外CompletableFuture還提供了專為兩個(gè)任務(wù)處理的方法
acceptEither

 CompletableFuture<String> completableFuture  = CompletableFuture.supplyAsync(()->randomDelay());
        completableFuture.acceptEither(completableFuture.supplyAsync(()->randomDelay()),u-> System.out.println(u)).join();
ForkJoinPool.commonPool-worker-2 sleep in 935
ForkJoinPool.commonPool-worker-1 sleep in 2422
ForkJoinPool.commonPool-worker-2 sleep in end
ForkJoinPool.commonPool-worker-2 return

CompletableFuture 異常處理

除了在get的時(shí)候通過 try catch 處理異常,CompletableFuture 提供了更優(yōu)雅的方式 exceptionally()和 handle()。handle處理方法類似,都是把異常對(duì)象轉(zhuǎn)為我們所需要的其他類型對(duì)象,然后處理。

  public static String getTestResult()
    {
        int i = 10/0;
        return "test";
    }

    public static void main(String[] args) {
        CompletableFuture<String> completableFuture  = new CompletableFuture();
        new Thread(()->{
            try {
                completableFuture.complete(getTestResult());
            } catch (Exception e) {
                System.out.println("get exception in side");
                completableFuture.completeExceptionally(e);
            }
        }).start();
        completableFuture.exceptionally(e->"we hava a exception"+e.getMessage())
                .thenAccept(u-> System.out.println(u));
    }

總結(jié)

有關(guān)CompletableFuture 大部分內(nèi)容已經(jīng)講完了,相信看了之后應(yīng)該都會(huì)用了吧!
更多的細(xì)節(jié)還是參考JAVA8官方文檔和源碼吧!

http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,869評(píng)論 18 139
  • CompletableFuture類實(shí)現(xiàn)了CompletionStage和Future接口。Future是Java...
    數(shù)齊閱讀 62,867評(píng)論 9 82
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,754評(píng)論 18 399
  • 這些天我比較忙!忙著自制中藥膏,因?yàn)榭评镉幸粋€(gè)糖足的患者,他的腳丫子實(shí)在是讓我頭疼,聽說臨漳有個(gè)治腳不錯(cuò)的,我去看...
    靈魂深處是無言閱讀 230評(píng)論 0 0
  • 今天的我終于出門了,心情好了許多,每次跟姐姐們?cè)谝黄穑矣X得我就能學(xué)到好多東西,通過聽她們的聊天,我覺得我還是太...
    簡愛De閱讀 172評(píng)論 0 0