介绍了CompletableFuture的特性以及一些使用方法,今天我们主要来聊一聊CompletableFuture的回调功能以及异步工作原理是如何实现的。
1.CompletableFuture类结构主要有两个属性
public class CompletableFuture implements Future, CompletionStage {
volatile Object result; // Either the result or boxed AltResult
volatile Completion stack; // Top of Treiber stack of dependent actions
...
}
复制代码
2.stack属性栈结构
abstract static class Completion extends ForkJoinTask
implements Runnable, AsynchronousCompletionTask {
volatile Completion next; // Treiber stack link
...
}
复制代码
3.UniCompletion的内部结构
abstract static class UniCompletion extends Completion {
Executor executor; // executor to use (null if none)
CompletableFuture dep; // the dependent to complete
CompletableFuture src; // source for action
...
}
复制代码
UniCompletion继承Completion类,包含以下几个参数:
这里为了方便讲解,我们用以下简短的代码来进行分析:
public static void main(String[] args) {
CompletableFuture baseFuture = CompletableFuture.completedFuture("Base Future");
log.info(baseFuture.thenApply((r) -> r + " Then Apply").join());
baseFuture.thenAccept((r) -> log.info(r)).thenAccept((Void) -> log.info("Void"));
}
复制代码
CompletableFuture
对象,再执行baseFuture.thenApply()
调用后会进行一个入栈操作,如下图baseFuture
引用的CompletableFuture
的stack
属性将会指向baseFuture.thenApply()
结果返回的新CompletableFuture
对象,而新CompletableFuture
对象的src
属性将指向来源CompletableFuture
即baseFuture
所引用的对象。2.在上一步的基础上再执行下一行代码,结果的引用关系图如下图:
在执行完baseFuture.thenAccept()
的时候,thenAccept
返回的任务将被压入栈顶,next
指向上一个代码段的返回对象,在thenAccept
返回的新CompletableFuture
对象中在进行一次thenAccept
的调用,就再产生一个新的CompletableFuture
对象,dept
属性就指向最新的CompletableFuture
对象。
public CompletableFuture thenApply(
Function super T,? extends U> fn) {
return uniApplyStage(null, fn);
}
private CompletableFuture uniApplyStage(
Executor e, Function super T,? extends V> f) {
if (f == null) throw new NullPointerException();
// 创建一个新的CompletableFuture对象
CompletableFuture d = new CompletableFuture();
// e:如果是异步调用直接执行代码块
// !d.uniApply:执行任务,如果返回false则任务未执行需入栈
if (e != null || !d.uniApply(this, f, null)) {
UniApply c = new UniApply(e, d, this, f);
// 创建出新的UniApply对象进行入栈
push(c);
// 尝试执行任务
c.tryFire(SYNC);
}
return d;
}
final boolean uniApply(CompletableFuture a,
Function super S,? extends T> f,
UniApply c) {
Object r; Throwable x;
// 任务未完成结果为null直接返回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())
// 任务未执行返回false
return false;
@SuppressWarnings("unchecked") S s = (S) r;
// 任务执行完成将结果写入result
completeValue(f.apply(s));
} catch (Throwable ex) {
completeThrowable(ex);
}
}
return true;
}
复制代码
以上代码片段主要描述了CompletableFuture
在执行任务时会创建出新的CompletableFuture
对象,使用新对象执行任务并获取结果使用CAS
写入到result
属性,如果任务未执行将压入栈顶,再重新尝试任务执行,在CompletableFuture
其他方法的调用也都大同小异,这里不在逐一分析,可自行打开源码阅读便于理解。
需要进行CompletableFuture异步调用则要使用Async
结尾的方法执行任务,这里我们就拿thenAcceptAsync()
的源码进行分析。
public CompletableFuture thenAcceptAsync(Consumer super T> action) {
return uniAcceptStage(asyncPool, action);
}
private CompletableFuture uniAcceptStage(Executor e, Consumer super T> f) {
if (f == null) throw new NullPointerException();
CompletableFuture d = new CompletableFuture();
// 如果是异步任务,这里的参数e不会为空,也就是会将任务执行压入栈顶
if (e != null || !d.uniAccept(this, f, null)) {
UniAccept c = new UniAccept(e, d, this, f);
push(c);
// 重点还是这个入口
c.tryFire(SYNC);
}
return d;
}
static final class UniAccept extends UniCompletion {
Consumer super T> fn;
UniAccept(Executor executor, CompletableFuture dep,
CompletableFuture src, Consumer super T> fn) {
super(executor, dep, src); this.fn = fn;
}
final CompletableFuture tryFire(int mode) {
CompletableFuture d; CompletableFuture a;
// dep为空即任务已被执行过,直接返回null
// uniAccept()结果为false,可能是任务执行中未完成,也可能是由线程池中的其他任务执行完成
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);
}
}
final CompletableFuture postFire(CompletableFuture> a, int mode) {
if (a != null && a.stack != null) {
// postComplete调用过来的,或者上一个任务执行完成,清空栈数据,否则调用postComplete完成任务
if (mode < 0 || a.result == null)
a.cleanStack();
else
// 完成任务执行并进行出栈
a.postComplete();
}
if (result != null && stack != null) {
if (mode < 0)
// postComplete调用过来的任务已完成
return this;
else
// 完成任务执行并进行出栈
postComplete();
}
return null;
}
复制代码
CompletableFuture
进行异步主要是通过将任务压入栈顶后tryFire
方法进行异步处理,如果任务未被执行则会通过postFire
方法有线程池中的线程进行任务执行,任务执行结果再使用CAS
将结果返回到result
,其他线程即可得知任务是否被执行过,如果当前现场判断当前任务为被执行,则调用postComplete()
执行完成任务。
CompletableFuture
通过异步回调的方式,解决了开发过程中异步调用获取结果的难点。开发人员只需接触到CompletableFuture
对象,以及CompletableFuture
任务的执行结果,无需设计具体异步回调的实现,并可通过自定义线程池进一步优化任务的异步调用。