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系列方法執行後,是如何保存節點信息,異常情況,其他信息如何處理等;