Java8新特性之:CompletableFuture

一. CompletableFuturejava

    1.Future接口網絡

        Future設計的初衷:對未來某個時刻會發生的結果進行建模。多線程

        它建模了一種異步計算,返回一個執行運算結果的引用,當運算結束後,這個引用被返回給調用方。在Future中出發那些潛在耗時的操做把調用線程解放出來,讓它能繼續執行其餘有價值的工做,再也不須要等待耗時的操做完成。異步

        Future的優勢:比更底層的Thread更易用。ide

        要使用Future,一般只須要將耗時的操做封裝在一個Callable對象中,再將它提交給ExecutorService。函數

ExecutorService executor = Executors.newCachedThreadPool();
Future<Double> future = executor.submit(new Callable<Double>() { //向ExecutorService提交一個Callable對象
    @Override
    public Double call() throws Exception {
        return doSomeLongComputation(); //以異步方式在新的線程中執行耗時的操做
    }
    });
doSomethingElse(); //異步操做進行的同時,你能夠作其餘的事情
try {
    //獲取異步操做的結果,若是最終被阻塞,沒法獲得結果,那麼在最多等待1秒鐘以後退出
    Double result = future.get(1, TimeUnit.SECONDS);
} catch (ExecutionException ee) {
    //加上拋出一個異常
} catch (InterruptedException ie) {
    //當前線程在等待過程當中被中斷
} catch (TimeoutException te) {
    //在Future對象完成以前超過已過時
}


    2. 實現異步API、代碼避免阻塞spa

        使用工廠方法supplyAsync建立CompletableFuture線程

public Future<Double> getPriceAsync2(String product) {
    return CompletableFuture.supplyAsync(() -> calculatePrice(product));
}


            supplyAsync方法接受一個生產者(Supplier)做爲參數,返回一個CompletableFuture對象,該對象完成異步執行後會讀取調用生產者方法的返回值。設計

            生產者方法會交由ForkJoinPool池中的某個執行線程(Executor)運行,可是你也可使用supplyAsync方法的重載版本,傳遞第二個參數指定不一樣的執行線程執行生產者方法。對象

        join方法等待異步操做結束

            CompletableFuture類中的join方法和Future接口中的get有相同的含義,等待運行結束。而且也聲明在Future接口中,惟一的不一樣是join方法不會拋出任何檢測到的異常。所以使用它時不須要再使用try/catch語句塊。

public List<String> findPrices(String product) {
//使用CompletableFuture以異步方式計算每種商品的價格
List<CompletableFuture<String>> priceFutures = shops.stream()
.map(shop -> CompletableFuture.supplyAsync(() -> shop.getName() + " price is " + shop.getPriceAsync(product)))
.collect(Collectors.toList());
//等待全部異步操做結束
return priceFutures.stream().
map(CompletableFuture::join)
.collect(Collectors.toList());
}


        使用定製執行器

            建立一個配有線程池的執行器。

            線程數的選擇:N(threads) = N(CPU) * U(CPU) * (1 + W/C)

                -- N(CPU):處理器的核的數目,能夠經過Runtime.getRuntime().availableProcessors()獲得;

                -- U(CPU):指望的CPU利用率(該值應該介於0和1之間);

                -- W/C:等待時間與計算時間的比率。

//建立一個線程池,線程池中線程的數目爲100和商店數目兩者中較小的一個值(這裏100爲線程池的上限)
private final Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true); //使用守護線程--這種方式不會阻止程序的關停
return t;
}
});


            集合進行並行計算有兩種方式:並行流和CompletableFutures。

                -- 計算密集型操做,而且沒有I/O,推薦使用Stream接口。由於實現簡單,同時效率也多是最高的(若是全部的線程都是計算密集型的,那就沒有必要建立比處理器核數更多的線程);

                -- 若是並行的工做單元還涉及等待I/O的操做(包括網絡鏈接等待),那麼使用CompletableFuture靈活性更好。這種狀況下處理流的流水線中若是發生I/O等待,流的延遲特性會讓咱們很難判斷到底何時觸發了等待。

    3. 對多個異步任務進行流水線操做

        thenApply:將一個由字符串轉換Quote的方法做爲參數傳遞給他

        thenCompose:該方法容許你對兩個異步操做進行流水線,第一個操做完成時,將其結果做爲參數傳遞給第二個操做。

                    對第一個CompletableFuture對象調用thenCompose,並向其傳遞一個函數。當第一個CompletableFuture執行完畢後,他的結果將做爲該函數的參數,這個函數的返回值是以第一個CompletableFuture的返回作輸入計算出的第二    個CompletableFuture對象。

                    使用thenCompose減小不少線程切換開銷。

        thenCombine:將兩個CompletableFuture對象結果整合起來。該方法接收名爲BiFunction的第二參數,這個參數定義了兩個CompletableFuture對象完成計算後,如何合併。

        thenAccept:方法接收CompletableFuture執行完畢後的返回值作參數。沒必要等待那些還未返回的結果。

相關文章
相關標籤/搜索