Java8筆記5

                                    CompletableFuture: 組合式異步編程

1.先了解下什麼是吞吐量:

吞吐量是指對網絡、設備、端口、虛電路或其他設施,單位時間內成功地傳送數據的數量(以比特字節、分組等測量)。(百科)

2.併發,並行:

併發:單個處理器核在多個任務之間切換處理

並行:多個處理器核同時處理多個任務

Future 接口

Future接口建模了一種異步計算,返回一個執行運算結果的引用,當運算結束後,這個運用hui被返回調用方。

(簡單說就是你去奶茶店排隊買奶茶,店員給你一個序號,你就不用在那裏等待,可以去做其他事,等好了再去拿,這就是異步)

例子:

ExecutorService executor = Executors.newCachedThreadPool(); 
Future<Double> future = executor.submit(new Callable<Double>() { 
 public Double call() {
 return doSomeLongComputation(); 
 }}); 
doSomethingElse();
try { 
 Double result = future.get(2, TimeUnit.SECONDS); 
} catch (Exception e) {
 // 計算拋出一個異常
}
        這種編程方式讓你的線程可以在ExecutorService以併發方式調 用另一個線程執行耗時操作的同時,去執行一些其他的任務。接着,如果你已經運行到沒有異步 操作的結果就無法繼續任何有意義的工作時,可以調用它的get方法去獲取操作的結果。如果操 作已經完成,該方法會立刻返回操作的結果,否則它會阻塞你的線程,直到操作完成,返回相應 的結果。
       Feature 使用重 載版本的get方法,它接受一個超時的參數,通過它,你可以定義你的線程等待Future結果的最 長時間,而不是一直等待下去

 使用 CompletableFuture 構建異步應用

       我們知道Future接口提供了方法來檢測異步計算是否已經結束(使用 isDone方法),等待異步操作結束,以及獲取計算的結果。但是這些特性還不足以讓你編寫簡潔 的併發代碼。比如,我們很難表述Future結果之間的依賴性;從文字描述上這很簡單,“當長時 間計算任務完成時,請將該計算的結果通知到另一個長時間運行的計算任務,這兩個計算任務都 完成後,將計算的結果與另一個查詢操作結果合併”。但是,使用Future中提供的方法完成這樣 的操作又是另外一回事。這也是我們需要更具描述能力的特性的原因,比如下面這些。
         將兩個異步計算合併爲一個——這兩個異步計算之間相互獨立,同時第二個又依賴於第 一個的結果。
         等待Future集合中的所有任務都完成。
         僅等待Future集合中最快結束的任務完成(有可能因爲它們試圖通過不同的方式計算同 一個值),並返回它的結果。
         通過編程方式完成一個Future任務的執行(即以手工設定異步操作結果的方式)。
         應對Future的完成事件(即當Future的完成事件發生時會收到通知,並能使用Future
      計算的結果進行下一步的操作,不只是簡單地阻塞等待操作的結果)。下面你會了解新的CompletableFuture類(它實現了Future接口)如何利用Java 8 的新特性以更直觀的方式將上述需求都變爲可能。StreamCompletableFuture的設計都遵循
了類似的模式:它們都使用了Lambda表達式以及流水線的思想。從這個角度,你可以說 CompletableFuture和Future的關係就跟StreamCollection的關係一樣。
(1)runAsync方法: 異步創建一個簡單的沒有返回值的Future
  ExecutorService executorService = Executors.newCachedThreadPool();

        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {

            System.out.println(Thread.currentThread().getName());
        }, executorService);

        System.out.println("主線程: " + Thread.currentThread().getName());
        future.get(2, TimeUnit.SECONDS);

結果:


主線程: main
pool-1-thread-1

(2)supplyAsync方法: 異步創建一個有返回值的CompleteFuture

 ExecutorService executorService = Executors.newCachedThreadPool();

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {

            System.out.println(Thread.currentThread().getName());
            return 4;
        }, executorService);

        System.out.println("主線程: " + Thread.currentThread().getName());
        Integer integer = future.get(2, TimeUnit.SECONDS);
        System.out.println(integer);

結果:


主線程: main
pool-1-thread-1
4

(3)thenApply, thenAccept, thenRun,這三個都是同步操作

thenApply() 監聽future返回,調用Future方法對返回值業務邏輯操作,這個操作有返回值,比如轉換類型

thenAccept() 監聽future返回,調用Consumer處理返回值,處理的結果沒有返回值,比如打印結果,返回值爲CompletableFuture<void>

thenRun() 監聽future返回,然後自己自定義處理,返回值爲CompletableFuture<void>

 ExecutorService executorService = Executors.newCachedThreadPool();

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {

            System.out.println(Thread.currentThread().getName());
            return 4;
        }, executorService).thenApply(f ->{
            
            System.out.println(Thread.currentThread().getName());
            System.out.println("thenApply: + " + f);
            return String.valueOf(f) + "aaa";
        });
        
        CompletableFuture<Void> voidCompletableFuture = future.thenAccept(f -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("thenAccept: + " + f);
        });

        CompletableFuture<Void> voidCompletableFuture1 = voidCompletableFuture.thenRun(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("thenRun: + ");
        });

        System.out.println("主線程: " + Thread.currentThread().getName());
        String integer = future.get(2, TimeUnit.SECONDS);
        System.out.println(integer);
pool-1-thread-1
主線程: main
pool-1-thread-1
thenApply: + 4
pool-1-thread-1
thenAccept: + 4aaa
pool-1-thread-1
thenRun: + 
4aaa

(4)thenApplyAsync, thenAcceptAsync, thenRunAsync,同上面三個對應,是異步操作

  ExecutorService executorService = Executors.newCachedThreadPool();

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {

            System.out.println(Thread.currentThread().getName());
            return 4;
        }, executorService).thenApplyAsync(f ->{

            System.out.println(Thread.currentThread().getName());
            System.out.println("thenApply: + " + f);
            return String.valueOf(f) + "aaa";
        }, executorService);

        CompletableFuture<Void> voidCompletableFuture = future.thenAcceptAsync(f -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("thenAccept: + " + f);
        }, executorService);

        CompletableFuture<Void> voidCompletableFuture1 = voidCompletableFuture.thenRunAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("thenRun: + ");
        }, executorService);

        System.out.println("主線程: " + Thread.currentThread().getName());
        String integer = future.get(2, TimeUnit.SECONDS);
        System.out.println(integer);
pool-1-thread-1
pool-1-thread-1
thenApply: + 4
pool-1-thread-1
thenAccept: + 4aaa
主線程: main
pool-1-thread-1
thenRun: + 
4aaa

注意:這裏是同一個線程,主要是因爲這裏線程池我用的是可緩存的線程池,線程池爲無限大,當執行第二個任務時第一個任務已經完成,會複用執行第一個任務的線程,而不用每次新建線程。我們可以讓線程睡眠一點時間

修改下代碼:

ExecutorService executorService = Executors.newCachedThreadPool();

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
            return 4;
        }, executorService).thenApplyAsync(f ->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
            System.out.println("thenApply: + " + f);
            return String.valueOf(f) + "aaa";
        }, executorService);

        CompletableFuture<Void> voidCompletableFuture = future.thenAcceptAsync(f -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
            System.out.println("thenAccept: + " + f);
        }, executorService);

        CompletableFuture<Void> voidCompletableFuture1 = voidCompletableFuture.thenRunAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
            System.out.println("thenRun: + ");
        }, executorService);

        System.out.println("主線程: " + Thread.currentThread().getName());
        String integer = future.get(4, TimeUnit.SECONDS);
        voidCompletableFuture.get();
        voidCompletableFuture1.get();
        System.out.println(integer);
主線程: main
pool-1-thread-1
pool-1-thread-2
thenApply: + 4
pool-1-thread-1
thenAccept: + 4aaa
pool-1-thread-2
thenRun: + 
4aaa

可以看到還是存在有些線程已經完成並且被複用了,可以再試試把睡眠時間調整爲不一樣試試。

(3) thenCompose方法 多層結構的future返回一個結果,跟java8的flatmap差不多

 CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
            return 4;
        }, executorService).thenCompose(f -> CompletableFuture.supplyAsync(() -> f + "dddd", executorService));

        System.out.println(stringCompletableFuture.get());
pool-1-thread-2
4dddd

(4)thenCombine與thenAcceptBoth, 合併兩個future,thenCombine有返回值,thenAcceptBoth沒有返回值

ExecutorService executorService = Executors.newCachedThreadPool();
        CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
            return 4;
        }, executorService);
        CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
            return 5;
        }, executorService);
        CompletableFuture<Integer> f3 = f1.thenCombine(f2, (s1, s2) -> {
            System.out.println(String.format("%d,%d", s1, s2));
            return s1 + s2;
        });
        System.out.println(f3.get());

        CompletableFuture<Void> f4 = f1.thenAcceptBoth(f2, (s1, s2) -> {
            System.out.println(String.format("%d,%d", s1, s2));
        });
        System.out.println(f4.get());
pool-1-thread-1
pool-1-thread-2
4,5
9
4,5
null

(5) allOf   anyOf  

allOf   CompleteableFuture數組裏面全部完成才返回結果

 ExecutorService executorService = Executors.newCachedThreadPool();
        List<CompletableFuture<String>> list = IntStream.rangeClosed(0, 10).mapToObj(i -> CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(i * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "sleep :" + i * 1000 + "s");
            return i + "s";
        }, executorService)).collect(Collectors.toList());
        CompletableFuture<Void> f1 = CompletableFuture.allOf(list.toArray(new CompletableFuture[]{}));
        System.out.println("全部已經完成了!!" + f1.get());
pool-1-thread-1sleep :0s
pool-1-thread-2sleep :1000s
pool-1-thread-3sleep :2000s
pool-1-thread-4sleep :3000s
pool-1-thread-5sleep :4000s
pool-1-thread-6sleep :5000s
pool-1-thread-7sleep :6000s
pool-1-thread-8sleep :7000s
pool-1-thread-9sleep :8000s
pool-1-thread-10sleep :9000s
pool-1-thread-11sleep :10000s
全部已經完成了!!null

anyOf CompleteableFuture數組裏面其中一個完成就返回結果

 ExecutorService executorService = Executors.newCachedThreadPool();
        List<CompletableFuture<String>> list = IntStream.rangeClosed(0, 10).mapToObj(i -> CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(i * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "sleep :" + i * 1000 + "s");
            return i + "s";
        }, executorService)).collect(Collectors.toList());
        CompletableFuture<Object> f1 = CompletableFuture.anyOf(list.toArray(new CompletableFuture[]{}));
        System.out.println("其中一個已經完成了!!" + f1.get());
pool-1-thread-1sleep :0s
其中一個已經完成了!!0s

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章