JAVA異步編程-JDK-CompletableFuture實踐與原理

1.文章目錄

  • ComPletableFuture概述
  • CompletableFuture實踐
  • CompletableFuture主要源碼導讀

2.CompletableFuture概述

  • CompleatableFuture是對Future的增強,我們知道Future的侷限性,CompleatableFuture可以通過編程方式顯式設置計算結果和狀態,並且可以作爲一個計算階段,當他完成時還能觸發另一個函數/行爲;
  • 當多個線程調用CompletableFuture的complete,cancel方式只有一個線程會成功;
  • CompletableFuture實現了CompletionStage接口方法:主要是當CompletableFuture任務結束後,同步使用任務來執行依賴任務結果的函數/行爲
  • 所有異步的方法在沒有執行Executor參數情況下,都是用ForkJoinPool.commonPool線程池來執行;
  • 所有實現CompletionStage的方法都是相互獨立的實現,不會因爲重載其他方法影響;
  • 另外:CompletableFuture的結果有一些任務依賴於此,利用的無鎖棧結構(CAS實現),因此執行順序和依賴順序相反;

3.CompletableFuture實踐

顯式設置CompletableFuture結果


public class CompletableFutureTest {
    // 自定義線程池
    private final static int CPU_COUNT = Runtime.getRuntime().availableProcessors();

    private final static ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(CPU_COUNT, 2 * CPU_COUNT, 1,
            TimeUnit.MINUTES, new LinkedBlockingQueue<>(5), new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] argv) throws ExecutionException, InterruptedException {
        CompletableFuture<String> completableFuture = new CompletableFuture<String>();
        POOL_EXECUTOR.execute(()->{
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        // 設置future結果
        System.out.println("---" + Thread.currentThread().getName() + " set future result --- ");
        completableFuture.complete("niuBi");

        // 等待異步執行結果
        System.out.println("main wait future result");
        System.out.println(completableFuture.get());
        System.out.println("main get future result");

        // 關閉線程池
        POOL_EXECUTOR.shutdown();
    }
}
  • 創建線程池,提交任務到異步線程池,主線程調用get等待線程執行完任務,會阻塞主線程;

基於CompletableFuture實現異步計算與結果轉化

  • 基於runAsync系列方法實現無返回值的異步計算;
  • 異步打印日誌,Dubbo異步存放配置信息,異步消息通知等模型
public class CompletableFutureTest {
    
    public static void runAsync() throws ExecutionException, InterruptedException {
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("over");
            }
        });

        System.out.println(completableFuture.get());
    }
    public static void main(String[] argv) throws ExecutionException, InterruptedException {
       runAsync();
    }
}
  • future.get獲取的是null,我們知道默認是ForkJoinPool.commonPool線程池,我們也可以自己實現線程池;
public class CompletableFutureTest {
    // 自定義線程池
    private final static int CPU_COUNT = Runtime.getRuntime().availableProcessors();

    private final static ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(CPU_COUNT, 2 * CPU_COUNT, 1,
            TimeUnit.MINUTES, new LinkedBlockingQueue<>(5), new ThreadPoolExecutor.AbortPolicy());


    public static void runAsync() throws ExecutionException, InterruptedException {
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("over");
            }
        }, POOL_EXECUTOR);

        System.out.println(completableFuture.get());
    }
    public static void main(String[] argv) throws ExecutionException, InterruptedException {

       runAsync();
    }
}
  • 基於supplyAsync系列方法實現有返回值的異步計算
  • future.get返回"niuBi",也可以轉化爲自定義線程池執行異步任務
public static void supplyAsync() throws ExecutionException, InterruptedException {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(new Supplier<String>() {

            @Override
            public String get() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 返回異步計算
                return "niuBi";
            }
        });

        System.out.println(completableFuture.get());
    }

自定義線程池:完成有返回值異步計算

public class CompletableFutureTest {
    // 自定義線程池
    private final static int CPU_COUNT = Runtime.getRuntime().availableProcessors();

    private final static ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(CPU_COUNT, 2 * CPU_COUNT, 1,
            TimeUnit.MINUTES, new LinkedBlockingQueue<>(5), new ThreadPoolExecutor.AbortPolicy());


  
    public static void supplyAsync() throws ExecutionException, InterruptedException {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(new Supplier<String>() {

            @Override
            public String get() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 返回異步計算
                return "niuBi";
            }
        }, POOL_EXECUTOR);

        System.out.println(completableFuture.get());
    }
    public static void main(String[] argv) throws ExecutionException, InterruptedException {

       supplyAsync();
       POOL_EXECUTOR.shutdown();
    }
}
  • 基於thenRun實現異步任務A,執行完畢後,激活任務B,這種方式激活的異步任務B拿不到任務A的執行結果;
public static void thenRun() throws ExecutionException, InterruptedException {
        CompletableFuture<String> oneFuture = CompletableFuture.supplyAsync(new Supplier<String>() {

            @Override
            public String get() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("one future end");
                // 返回異步結果
                return "hello, NiuBi";
            }
        });
        CompletableFuture<Void> twoFuture = oneFuture.thenRun(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("two Future end");
        });
        // 同步等待twoFuture結束
        System.out.println(twoFuture.get());
    }
  • 基於thenAccept實現異步任務A,執行完後,激活任務B,B獲取A的結果搞事情;
  • future.get的結果爲null;
public static void thenAccept() throws ExecutionException, InterruptedException {
        CompletableFuture<String> oneFuture = CompletableFuture.supplyAsync(new Supplier<String>() {

            @Override
            public String get() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("one future end");

                return "niuBi";
            }
        });
        CompletableFuture<Void> twoFuture = oneFuture.thenAccept(new Consumer<String>() {
            @Override
            public void accept(String result) {
                // 對oneFuture的結果操作
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("two future get one future result : " + result + " add ");
            }
        });

        System.out.println(twoFuture.get());
    }
  • 基於thenApply實現任務A,完成後,激活任務B執行,B可以拿到A的結果,並且我們可以獲得B的結果
  • 默認的oneFuture以及回調事件twoFuture在ForkJoin線程池是同一線程,如果用自定義線程池,調度的線程可能不一樣;

    public static void thenApply() throws ExecutionException, InterruptedException {
        CompletableFuture<String> oneFuture = CompletableFuture.supplyAsync(new Supplier<String>() {

            @Override
            public String get() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("one future end");

                return "niuBi";
            }
        });

        CompletableFuture<String> twoFuture = oneFuture.thenApply(
                // arg1:前任務的返回結果類型,arg2:回調函數的返回類型
                new Function<String, String>() {
            @Override
            public String apply(String preResult) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return preResult + " two future add";
            }
        });

        System.out.println(twoFuture.get());
    }
  • 基於whenComplete設置回調函數:當異步任務執行完畢後進行回調,不會阻塞調用線程;
public class CompletableFutureTestDubbo {

    private static final CountDownLatch  countDownLatch = new CountDownLatch(1);
    public static void main(String[] argv) throws InterruptedException {
        CompletableFuture<String> oneFuture = CompletableFuture.supplyAsync(new Supplier<String>() {

            @Override
            public String get() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
                return "niuBi";
            }
        });

        oneFuture.whenComplete(new BiConsumer<String, Throwable>() {
            @Override
            public void accept(String s, Throwable throwable) {
                // 如果沒有異常
                if (null == throwable){
                    System.out.println(s);
                } else {
                    throwable.printStackTrace();
                }
            }
        });

        countDownLatch.await();
    }
}
  • 多個CompletableFuture進行組合運算
  • 基於thenCompose實現當一個CompletableFuture執行完畢,執行另一個CompletableFuture
public class CompletableFutureTestDubbo {

    private static final CountDownLatch  countDownLatch = new CountDownLatch(1);
    public static CompletableFuture<String> doSomethingOne(String encodedCompanyId){
        return CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return encodedCompanyId;
            }
        });
    }

    public static CompletableFuture<String> doSomethingTwo(String companyId){
        return CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return companyId + "阿里雲";
            }
        });
    }
    public static void main(String[] argv) throws InterruptedException, ExecutionException {
        CompletableFuture<String> completableFuture = doSomethingOne("123").thenCompose(id -> doSomethingTwo(id));
        System.out.println(completableFuture.get());
    }
}
  • 基於thenCombine實現:兩個併發任務結束,使用兩者的結果作爲參數;
public class CompletableFutureTestDubbo {

    private static final CountDownLatch  countDownLatch = new CountDownLatch(1);
    public static CompletableFuture<String> doSomethingOne(String encodedCompanyId){
        return CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return encodedCompanyId;
            }
        });
    }

    public static CompletableFuture<String> doSomethingTwo(String companyId){
        return CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return companyId + "阿里雲";
            }
        });
    }
    public static void main(String[] argv) throws InterruptedException, ExecutionException {
      
        CompletableFuture<Object> completableFuture = doSomethingOne("123").thenCombine(doSomethingTwo("456"), (oneResult, twoResult) -> {
                return oneResult + ":" + twoResult;
        });
        System.out.println(completableFuture.get());
    }
}
  • 基於AllOf等待所有併發任務結束
public class CompletableFutureTestDubbo {

    private static final CountDownLatch  countDownLatch = new CountDownLatch(1);
    public static CompletableFuture<String> doSomethingOne(String encodedCompanyId){
        return CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("do SomethingOne");
                return encodedCompanyId;
            }
        });
    }

    public static CompletableFuture<String> doSomethingTwo(String companyId){
        return CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("do SomethingTwo");
                return companyId + "阿里雲";
            }
        });
    }
    public static void main(String[] argv) throws InterruptedException, ExecutionException {
    
        List<CompletableFuture<String>> futureList = new ArrayList<>();
        futureList.add(doSomethingOne("111"));
        futureList.add(doSomethingOne("222"));
        futureList.add(doSomethingOne("333"));
        futureList.add(doSomethingTwo("444"));
        futureList.add(doSomethingTwo("555"));
        CompletableFuture<Void> completableFuture = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()]));
        System.out.println(completableFuture.get());
    }
}
  • 基於anyof實現當任務集合中一個任務結束就會返回,但是任務都會運行完;
public class CompletableFutureTestDubbo {

    private static final CountDownLatch  countDownLatch = new CountDownLatch(1);
    public static CompletableFuture<String> doSomethingOne(String encodedCompanyId){
        return CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("do SomethingOne");
                return encodedCompanyId;
            }
        });
    }

    public static CompletableFuture<String> doSomethingTwo(String companyId){
        return CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("do SomethingTwo");
                return companyId + "阿里雲";
            }
        });
    }
    public static void main(String[] argv) throws InterruptedException, ExecutionException {
   
        List<CompletableFuture<String>> futureList = new ArrayList<>();
        futureList.add(doSomethingOne("111"));
        futureList.add(doSomethingOne("222"));
        futureList.add(doSomethingOne("333"));
        futureList.add(doSomethingTwo("444"));
        futureList.add(doSomethingTwo("555"));
        CompletableFuture<Object> completableFuture = CompletableFuture.anyOf(futureList.toArray(new CompletableFuture[futureList.size()]));
        System.out.println(completableFuture.get());
    }

異常處理

  • 當任務的處理過程出現異常,如何處理;
  • 如下代碼,我們拋出異常,導致future.get一直阻塞,線上操作是致命的;
public class CompletableFutureExpectionTest {
    public static void main(String[] argv) throws ExecutionException, InterruptedException {
        CompletableFuture<String> completableFuture = new CompletableFuture<>();
        new Thread(()->{
            try {
                if (true){
                    throw new RuntimeException("error cause");
                }
                completableFuture.complete("success");
            }catch (Exception e) {

            }
            System.out.println(Thread.currentThread().getName() + " set result");
        }, "threadA").start();

        System.out.println(completableFuture.get());
    }
}
  • completeExceptionAlly方法處理異常情況:如下代碼,會打印異常信息
public class CompletableFutureExpectionTest {
    public static void main(String[] argv) throws ExecutionException, InterruptedException {
        CompletableFuture<String> completableFuture = new CompletableFuture<>();
        new Thread(()->{
            try {
                if (true){
                    throw new RuntimeException("error cause");
                }
                completableFuture.complete("success");
            }catch (Exception e) {
                completableFuture.completeExceptionally(e);
            }
            System.out.println(Thread.currentThread().getName() + " set result");
        }, "threadA").start();

        //System.out.println(completableFuture.exceptionally(t-> "defalut").get());
         System.out.println(completableFuture.get());
    }
}
  • 以上是對於CompletableFuture的實踐,具體業務可以更加靈活的擴展,結合應用;

4.CompletableFuture源碼導讀

基本屬性:保存變量偏移量,CAS操作修改;

// Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long RESULT;
    private static final long STACK;
    private static final long NEXT;
    static {
        try {
            final sun.misc.Unsafe u;
            UNSAFE = u = sun.misc.Unsafe.getUnsafe();
            Class<?> k = CompletableFuture.class;
            RESULT = u.objectFieldOffset(k.getDeclaredField("result"));
            STACK = u.objectFieldOffset(k.getDeclaredField("stack"));
            NEXT = u.objectFieldOffset
                (Completion.class.getDeclaredField("next"));
        } catch (Exception x) {
            throw new Error(x);
        }
    }

reportget

 private static <T> T reportGet(Object r)
        throws InterruptedException, ExecutionException {
         // 如果r爲null則拋出異常
        if (r == null) // by convention below, null means interrupted
            throw new InterruptedException();
        // 如果是異常類
        if (r instanceof AltResult) {
            Throwable x, cause;
            // 根據異常類型,拋出異常
            if ((x = ((AltResult)r).ex) == null)
                return null;
            if (x instanceof CancellationException)
                throw (CancellationException)x;
            if ((x instanceof CompletionException) &&
                (cause = x.getCause()) != null)
                x = cause;
            throw new ExecutionException(x);
        }
        // 轉化結果
        @SuppressWarnings("unchecked") T t = (T) r;
        return t;
    }

線程池,Task運行本質

 // 默認爲forkjoinPool  
private static final Executor asyncPool = useCommonPool ?
        ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

    // Task本質,創建線程運行

    static final class ThreadPerTaskExecutor implements Executor {
        public void execute(Runnable r) { new Thread(r).start(); }
    }

Completion代表一個異步計算節點

abstract static class Completion extends ForkJoinTask<Void>
        implements Runnable, AsynchronousCompletionTask {
       // 節點鏈
        volatile Completion next;      // Treiber stack link

       
        abstract CompletableFuture<?> tryFire(int mode);

        /** Returns true if possibly still triggerable. Used by cleanStack. */
        abstract boolean isLive();
        //task的運行,其實就是方法中寫的run任務
        public final void run()                { tryFire(ASYNC); }
        public final boolean exec()            { tryFire(ASYNC); return true; }
        public final Void getRawResult()       { return null; }
        public final void setRawResult(Void v) {}
    }

runAsync(Runnable runnable):

 public static CompletableFuture<Void> runAsync(Runnable runnable) {
        return asyncRunStage(asyncPool, runnable);
    }

  static CompletableFuture<Void> asyncRunStage(Executor e, Runnable f) {
        // 判空校驗
        if (f == null) throw new NullPointerException();
        // 創建返回對象
        CompletableFuture<Void> d = new CompletableFuture<Void>();
         // 線程池執行
        e.execute(new AsyncRun(d, f));
        //返回future
        return d;
    }

   static final class AsyncRun extends ForkJoinTask<Void>
            implements Runnable, AsynchronousCompletionTask {
        CompletableFuture<Void> dep; Runnable fn;
        AsyncRun(CompletableFuture<Void> dep, Runnable fn) {
            this.dep = dep; this.fn = fn;
        }

        public final Void getRawResult() { return null; }
        public final void setRawResult(Void v) {}
        public final boolean exec() { run(); return true; }

        public void run() {
            CompletableFuture<Void> d; Runnable f;
            if ((d = dep) != null && (f = fn) != null) {
                dep = null; fn = null;
                // 任務沒有完成
                if (d.result == null) {
                    try {
                   // 運行
                        f.run();
                    //設置返回結果null
                        d.completeNull();
                    } catch (Throwable ex) {
                        d.completeThrowable(ex);
                    }
                }
                 //彈出依賴當前結果的節點,執行
                d.postComplete();
            }
        }
    }

CompletableFuture<U> supplyAsync(Supplier<U> supplier):有返回結果

    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
        return asyncSupplyStage(asyncPool, supplier);
    }

    static <U> CompletableFuture<U> asyncSupplyStage(Executor e,
                                                     Supplier<U> f) {
        if (f == null) throw new NullPointerException();
         // 創建future對象
        CompletableFuture<U> d = new CompletableFuture<U>();
         // 線程池執行AsyncSupply對象(包裝f)
        e.execute(new AsyncSupply<U>(d, f));
        // 返回future
            return d;
        }

         public void run() {
            CompletableFuture<T> d; Supplier<T> f;
            if ((d = dep) != null && (f = fn) != null) {
                dep = null; fn = null;
                if (d.result == null) {
                    try {
                        // f.get獲取值
                        d.completeValue(f.get());
                    } catch (Throwable ex) {
                        d.completeThrowable(ex);
                    }
                }
                // 通知依賴節點
                d.postComplete();
            }
  • 介紹l主要的運行任務方法;

int  getNumberOfDependents:獲取依賴該節點的任務數

 // 獲取依賴該節點的任務數 
public int getNumberOfDependents() {
        int count = 0;
        // 遍歷一遍
        for (Completion p = stack; p != null; p = p.next)
            ++count;
        return count;
    }
  • 基本的方法實現已經結束;

4.總結

  • CompletableFuture對異步編程的支持很強,可以根據我們的業務進行靈活的運用;在框架中異步思想的運行很多,因此對CompletableFuture的運用要非常熟悉,後續我們對CompletableFuture的所有方法細節實現進行導讀,如何通知其他依賴節點,then系列方法執行後,是如何保存節點信息,異常情況,其他信息如何處理等;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章