圖解CompletableFuture源碼

前言

關於CompletableFuture源碼解析部分,整體上感覺還是寫比較難的,不過爲了推廣到團隊還是要好好搞一下的,我還是希望大家看到這邊文章能學到點什麼,廢話不多說開始吧。

屬性部分

首先看屬性部分,我覺得可以從全貌瞭解他的整體的數據結構,後續我們看到一些操作的時候,也不會產生疑問,算是一種先整體後部分的思想。

打開CompletableFuture源碼以後我們首先看到是下面兩個核心的關鍵屬性result和stack,關於這兩個屬性也有核心的註釋,result可能是返回的結果集,也可能是包裝的AltResult,stack這個數據暴露出了CompletableFuture的整體的結構是一個棧。

    volatile Object result;       // Either the result or boxed AltResult
    volatile Completion stack;    // Top of Treiber stack of dependent actions

接下來我們看下Completion的情況,Completion是一個抽象類,分別實現了Runnable、AsynchronousCompletionTask接口,繼承了ForkJoinPoolTask類,而ForJoinPoolTask抽象類又實現了Future接口,因此Completion實際上就是一個Future。

img
img

在Completion類中還有一個非常重要的成員屬性,結合我們上面看到的CompletableFuture的stack屬性,整好能驗證CompletableFuture是一個鏈表的一個數據結構,Completion中的next保存了棧中下一個元素的引用,而CompletableFuture中的stack永遠指向棧頂,至於是不是棧我們可以看下後續方法是如何操作的。

  volatile Completion next;

關於Completion類其實是一個抽象類,還有很多的實現,如下圖,後續我們看到具體的實現的時候再來細化實現類。

img
img

核心方法源碼解析

首先我們來看兩個測試用例,

    @Test
    public void test1() throws ExecutionException, InterruptedException {
        CompletableFuture<String> base = new CompletableFuture<>();
        CompletableFuture<String> future = base.thenApply(s -> s + " 2").thenApply(s -> s + " 3");
        base.complete("1");
        System.out.println(future.get());
    }


    @Test
    public void test2() throws ExecutionException, InterruptedException {
        CompletableFuture<String> base = new CompletableFuture<>();
        CompletableFuture<String> future = base.thenApply(s -> s + " 2").thenApply(s -> s + " 3");
        future.complete("1");
        System.out.println(future.get());
    }

執行這兩個測試用例以後,我們會發現最終的結果的是不一致的,這裏base和future對象,分別調用complete()和get()方法的排列組合,最終導致結果就發生了變化,是不是很神奇,接下來我們就來看看thenApply相關源碼部分。

thenApply

關於thenApply的使用,CompletableFuture提供了類似的三個方法,以Async結尾的表示異步執行,如果傳入Executor則以指定線程池執行,否則默認使用的線程池是ForkJoinPool。

public <U> CompletableFuture<U> thenApply(
    Function<? super T,? extends U> fn)
 
{
    return uniApplyStage(null, fn);
}

public <U> CompletableFuture<U> thenApplyAsync(
    Function<? super T,? extends U> fn)
 
{
    return uniApplyStage(asyncPool, fn);
}

public <U> CompletableFuture<U> thenApplyAsync(
    Function<? super T,? extends U> fn, Executor executor)
 
{
    return uniApplyStage(screenExecutor(executor), fn);
    }

我們重點關注的thenApply的方法,整體的源碼如下:

    public <U> CompletableFuture<U> thenApply(
        Function<? super T,? extends U> fn)
 
{
        return uniApplyStage(null, fn);
    }

    private <V> CompletableFuture<V> uniApplyStage(
        Executor e, Function<? super T,? extends V> f)
 
{
        if (f == nullthrow new NullPointerException();
        1.創建一個新的CompletableFuture對象
        CompletableFuture<V> d =  new CompletableFuture<V>();
        if (e != null || !d.uniApply(this, f, null)) {
            2. 構建UniApply e代表線程池 d 代表新的CompletableFuture this 代表當前
                f 代表方法 這個時候 UniApply 內部的所有的引用都處於爲null的狀態
            UniApply<T,V> c = new UniApply<T,V>(e, d, this, f);
            3. c其實就是Completion對象,被push到棧中
            push(c);
            4. 嘗試執行c
            c.tryFire(SYNC);
        }
        5. 這個d會一直返回到調用thenApply的地方,後續的鏈式調用會作用在這個d上面
        return d;
    }

    @SuppressWarnings("serial")
    static final class UniApply<T,Vextends UniCompletion<T,V{
        Function<? super T,? extends V> fn;
        UniApply(Executor executor, CompletableFuture<V> dep,
                 CompletableFuture<T> src,
                 Function<? super T,? extends V> fn) {
            2.1 向上執行
            super(executor, dep, src); this.fn = fn;
        }
    }

    abstract static class UniCompletion<T,Vextends Completion {
        Executor executor;                 // executor to use (null if none)
        CompletableFuture<V> dep;          // the dependent to complete
        CompletableFuture<T> src;          // source for action

        UniCompletion(Executor executor, CompletableFuture<V> dep,
                      CompletableFuture<T> src) {
            2.2 dep就是新創建的d  src就是當前的this
            this.executor = executor; this.dep = dep; this.src = src;
        }
    }

關於執行第2步的時候,構建的對象如下圖, src和dep都是空的CompletableFuture,next爲Null,這裏我們會發現所有的都是繼承Completion對象,最終所有都是構建都可以理解爲Completion對象;

img
img
image.png
image.png

關於執行第3步的時候,構建的UniApply對象的內容完成壓棧的操作,將CompletableFuture的stack屬性指向Completion對象;

img
img
image.png
image.png

接下來看第4步操作,嘗試執行Completion;

    @SuppressWarnings("serial")
    static final class UniApply<T,Vextends UniCompletion<T,V{
        Function<? super T,? extends V> fn;
        UniApply(Executor executor, CompletableFuture<V> dep,
                 CompletableFuture<T> src,
                 Function<? super T,? extends V> fn) {
            super(executor, dep, src); this.fn = fn;
        }
        final CompletableFuture<V> tryFire(int mode) {
            4.1 d新創建的 a(也是c中的src) 就是原來的
            CompletableFuture<V> d; CompletableFuture<T> a;
            4.2 如果uniApply執行成功,則會進到下面的postFire調用
                否則返回null 如果返回null,就要等待以後的主動complete來再次觸發
            if ((d = dep) == null ||
                !d.uniApply(a = src, fn, mode > 0 ? null : this))
                return null;
            4.5 tryFire成功後,會把以下幾個屬性設爲null,表面此Completion已經完成任務,
                變成dead狀態
            dep = null; src = null; fn = null;
            4.6 出棧
            return d.postFire(a, mode);
        }
    }
    final <S> boolean uniApply(CompletableFuture<S> a,
                               Function<? super S,? extends T> f,
                               UniApply<S,T> c)
 
{
        Object r; Throwable x;
        4.3 如果a(也是c中的src)沒有準備完成,那result是空,這裏就會直接返回false
        if (a == null || (r = a.result) 
== null || f == null)
            return false;
        tryComplete: if (result == null) {
            if (r instanceof AltResult) {
                if ((x = ((AltResult)r).ex) != null) {
                    completeThrowable(x, r);
                    break tryComplete;
                }
                r = null;
            }
            try {
                if (c != null && !c.claim())
                    return false;
                @SuppressWarnings("unchecked") S s = (S) r;
                4.4 如果r不爲空,則會作爲f的輸入參數,f的輸出則成爲當前CompletableFuture的完成值
                completeValue(f.apply(s));
            } catch (Throwable ex) {
                completeThrowable(ex);
            }
        }
        return true;
    }

第5步返回d, 這個d會返回到調用thenApply的地方,後續的鏈式調用會作用在這個d上面,接下來我們可以看到base對象就是我們構建好的第一個鏈;

img
img

這裏我們可以猜測後續的執行thenApply的方法,也就是執行完成test1的第二行代碼,生成的結構如下圖:

img
img

接下來我們驗證一下,我們可以發現和我們猜想一致;

img
img

當我們的代碼執行到test1的第3行的時候,也就是complete方法,該方法也就是爲了解決我們執行tryFire執行失敗後動作,源碼如下:

    public boolean complete(T value) {
        boolean triggered = completeValue(value);
        postComplete();
        return triggered;
    }

    final void postComplete() {
        1this表示當前的CompletableFuture, 也就是我們base
        CompletableFuture<?> f = this; Completion h;
        2. 判斷stack是否爲空  或者如果f的棧爲空且不是this則重置
        while ((h = f.stack) != null ||
               (f != this && (h = (f = this).stack) != null)) {
            CompletableFuture<?> d; Completion t;
            3. CAS出棧
            if (f.casStack(h, t = h.next)) {
                if (t != null) { 4.出棧的h不是最後一個元素,最後一個元素直接執行7即可
                    if (f != this) {
                        5. 如果f不是this,將剛出棧的h, 入this的棧頂
                            我猜測這個地方大家會有迷惑
                        pushStack(h);
                        continue;
                    }
                    h.next = null;    6. detach
                }
                f = (d = h.tryFire(NESTED)) == null ? this : d;  7.調用tryFire
            }
        }
    }

對於postComplete()方法可以理解爲當任務完成之後,調用的一個後完成方法,主要用於觸發其他依賴任務,也就是完成出棧的操作,關於第4、5步和的疑惑,這裏我先說一下,這裏的原因是每次調用產生的Completion並不在同一個stack中,接下來我們來看一個複雜的案例,可能大家就比較明白了;

複雜案例

    @Test
    public void test3() throws ExecutionException, InterruptedException {
        CompletableFuture<String> base = new CompletableFuture<>();
        CompletableFuture<String> future = base.thenApply(s -> {
            log.info("2");
            return s + " 2";
        });
        base.thenAccept(s -> log.info(s + "3-1")).thenAccept(aVoid -> log.info("3-2"));
        base.thenAccept(s -> log.info(s + "4-1")).thenAccept(aVoid -> log.info("4-2"));
        base.complete("1");
        log.info("base result: {}", base.get());
        log.info("future result: {}", future.get());
    }

首先看下輸出,我們可以看到基本上是按照4-3-2-1的順序輸出的,證明CompletableFuture整體上是一個棧的結構,接下來我們就圖解下這一過程;

img
img

當代碼執行完第7行的時候我們得到的是這樣的結構:

img
img
image.png
image.png

代碼執行完第8行的時候,結構是這樣的:

img
img
image.png
image.png

執行完第9行的時候,結構是這樣的:

img
img
image.png
image.png

到這裏就構成我們整個的調用鏈路,這個時候我們可以想明白爲什麼出棧的時候要判斷下f != this了吧,因爲內部又嵌套層棧的結構,構成了一個圖狀;

當代碼執行到第10行的時候,就開始出棧,按照4-3-2-1的順序輸出,到這裏這部分內容就講解完成了。

參考以下內容:

深入理解JDK8新特性CompletableFuture

結束

歡迎大家點點關注,點點贊!

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