Java——CompletableFuture原理
1. CompletableFuture作用
CompletableFuture
的主要作用是弥补Future
需要阻塞获取结果,CompletableFuture
根据主任务执行结果自动执行依赖任务,无需阻塞主线程等待主任务执行完。当然是在CompletableFuture
这个操作定义是异步执行的,无需获取同步结果的前提下。
2. 源码分析
示例
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
System.out.println("开始煮米饭");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "熟饭";
}).thenAccept(result -> {
System.out.println("我们开始吃" + result);
});
System.out.println("先煎个鸡蛋");
future.join();
supplyAsync()开启主任务
定义任务AsyncSupply
,会将主任务交给线程池异步处理
static <U> CompletableFuture<U> asyncSupplyStage(Executor e,
Supplier<U> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<U> d = new CompletableFuture<U>();
// 异步执行主任务
e.execute(new AsyncSupply<U>(d, f));
return d;
}
既然可以在线程池中执行,直接看AsyncSupply.run()
volatile Object result;
@SuppressWarnings("serial")
static final class AsyncSupply<T> extends ForkJoinTask<Void>
implements Runnable, AsynchronousCompletionTask {
CompletableFuture<T> dep; Supplier<T> fn;
AsyncSupply(CompletableFuture<T> dep, Supplier<T> fn) {
this.dep = dep; this.fn = fn;
}
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 {
// 等待主任务执行完后保存到结果变量中
d.completeValue(f.get());
} catch (Throwable ex) {
d.completeThrowable(ex);
}
}
// 执行完主任务后通知依赖任务执行
d.postComplete();
}
}
}
final boolean completeValue(T t) {
return UNSAFE.compareAndSwapObject(this, RESULT, null,
(t == null) ? NIL : t);
}
final void postComplete() {
CompletableFuture<?> f = this; Completion h;
while ((h = f.stack) != null ||
(f != this && (h = (f = this).stack) != null)) {
CompletableFuture<?> d; Completion t;
if (f.casStack(h, t = h.next)) {
if (t != null) {
if (f != this) {
pushStack(h);
continue;
}
h.next = null;
}
// 执行依赖任务
f = (d = h.tryFire(NESTED)) == null ? this : d;
}
}
}
run()
主要逻辑:
-
等待执行主任务
Supplier.get()
,执行完后通过CAS将结果保存到result,注意这里result用了volatile修饰保证实时性,因为类中方法很多需要判断result是否为空来做操作; -
主任务执行成功后,通知执行依赖任务,在
postComplete()
中将循环类中Completion中的stack。Completion为一个链表结构,保存依赖的任务,在后面会讲依赖任务怎么加到这个链表中的。进行CAS出链表后,tryFire()
执行依赖任务。
thenAccept()添加依赖任务
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
return uniAcceptStage(null, action);
}
private CompletableFuture<Void> uniAcceptStage(Executor e,
Consumer<? super T> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<Void> d = new CompletableFuture<Void>();
// 是否立即执行
if (e != null || !d.uniAccept(this, f, null)) {
UniAccept<T> c = new UniAccept<T>(e, d, this, f);
// 加入堆栈中
push(c);
// 尝试执行
c.tryFire(SYNC);
}
return d;
}
// 加入Completion链表中,对应主任务完成后的postComplete()逻辑
final void push(UniCompletion<?,?> c) {
if (c != null) {
while (result == null && !tryPushStack(c))
lazySetNext(c, null); // clear on failure
}
}
final <S> boolean uniAccept(CompletableFuture<S> a,
Consumer<? super S> f, UniAccept<S> c) {
Object r; Throwable x;
// 主任务未执行完则不执行依赖任务
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;
// 执行依赖任务
f.accept(s);
completeNull();
} catch (Throwable ex) {
completeThrowable(ex);
}
}
return true;
}
逻辑:
uniAccept()
判断主任务是否执行完,执行完的话直接accept()
运行依赖任务;- 主任务没执行完的话,
uniAccept()
返回false,进入if逻辑中,构造UniAccept
加入到Completion
链表中,对应主任务执行完后的postComplete()
逻辑; - 最后调用
UniAccept.tryFire()
尝试执行依赖任务,其中也是调用步骤1
的uniAccept()
.
UniAccept执行依赖任务
@SuppressWarnings("serial")
static final class UniAccept<T> extends UniCompletion<T,Void> {
Consumer<? super T> fn;
UniAccept(Executor executor, CompletableFuture<Void> dep,
CompletableFuture<T> src, Consumer<? super T> fn) {
super(executor, dep, src); this.fn = fn;
}
final CompletableFuture<Void> tryFire(int mode) {
CompletableFuture<Void> d; CompletableFuture<T> a;
if ((d = dep) == null ||
!d.uniAccept(a = src, fn, mode > 0 ? null : this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
UniAccept
是Completion
其中的一个子类,Completion
还有很多子类,分别对应不同的依赖任务操作。其中主要方法是tryFire()
尝试执行依赖任务,实质也是调用了uniAccept()
.
3. 总结
CompletableFuture
原理为:异步执行主任务,将依赖任务加入到链表中,主任务执行完从链表中获取依赖任务执行。这时会有个疑问,依赖任务还没加入到链表中,主任务就执行完了,怎么办?所以添加依赖任务的时候,会判断主任务是否执行完,如果执行完则立即执行依赖任务,不添加到链表中。
世界那么大,感谢遇见,未来可期…
欢迎同频共振的那一部分人
作者公众号:Tarzan写bug