前言
關於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。
在Completion類中還有一個非常重要的成員屬性,結合我們上面看到的CompletableFuture的stack屬性,整好能驗證CompletableFuture是一個鏈表的一個數據結構,Completion中的next保存了棧中下一個元素的引用,而CompletableFuture中的stack永遠指向棧頂,至於是不是棧我們可以看下後續方法是如何操作的。
volatile Completion next;
關於Completion類其實是一個抽象類,還有很多的實現,如下圖,後續我們看到具體的實現的時候再來細化實現類。
核心方法源碼解析
首先我們來看兩個測試用例,
@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 == null) throw 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,V> extends 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,V> extends 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對象;
關於執行第3步的時候,構建的UniApply對象的內容完成壓棧的操作,將CompletableFuture的stack屬性指向Completion對象;
接下來看第4步操作,嘗試執行Completion;
@SuppressWarnings("serial")
static final class UniApply<T,V> extends 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對象就是我們構建好的第一個鏈;
這裏我們可以猜測後續的執行thenApply的方法,也就是執行完成test1的第二行代碼,生成的結構如下圖:
接下來我們驗證一下,我們可以發現和我們猜想一致;
當我們的代碼執行到test1的第3行的時候,也就是complete方法,該方法也就是爲了解決我們執行tryFire執行失敗後動作,源碼如下:
public boolean complete(T value) {
boolean triggered = completeValue(value);
postComplete();
return triggered;
}
final void postComplete() {
1. this表示當前的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整體上是一個棧的結構,接下來我們就圖解下這一過程;
當代碼執行完第7行的時候我們得到的是這樣的結構:
代碼執行完第8行的時候,結構是這樣的:
執行完第9行的時候,結構是這樣的:
到這裏就構成我們整個的調用鏈路,這個時候我們可以想明白爲什麼出棧的時候要判斷下f != this了吧,因爲內部又嵌套層棧的結構,構成了一個圖狀;
當代碼執行到第10行的時候,就開始出棧,按照4-3-2-1的順序輸出,到這裏這部分內容就講解完成了。
參考以下內容:
結束
歡迎大家點點關注,點點贊!