目录
complete/completeExceptionally
obtrudeValue/obtrudeExceptionlly
最近因为项目需要开发了合作代理网关,它的主要功能就是加解签、入参转换、路由转发(支持http/dubbo协议转发)、出参转换。第一版上线了一个阻塞版本(每次请求从接收到转发到返回线程始终被占用),网关一般底下都会挂很多业务系统,一个系统响应慢就会拖垮整个网关,所以我们进行了非阻塞化升级。在非阻塞升级中,dubbo的底层原本就支持异步调用-CompletableFuture。一直以来,没有觉得CompletableFuture有什么实际的作用,直到这次才发现其是这么美妙。CompletableFuture机制可以帮我们构建一条链。请求来的时候从起点开始构建这么一条链,当有响应时从终点开始依次把响应推到起点。
CompletionStage
CompletableFuture实现了2个类Future和CompletionStage。其中CompletionStage提供了异步计算的接口规范,CompletableFuture实现了这些规范。
当一个CompletionStage完成将触发直接依赖它的一批CompletionStage的执行,这批CompletionStage完成又触发各自下一批CompletionStage的执行并完成。CompletionStage构造了一条结果传递链来,当有结果产生时,结果从终点CompletionStage依次推向起点的CompletionStage,在结果传递过程中任何一个CompletionStage都可以对结果进行处理(包括异常处理、类型转换等待)。CompletionStage提供的规范可以构造非常简单的传递链也可以构造很复杂的传递链。传递链可以单个终点、单个起点;也可以多个终点、多个起点。
这条传递链很简单就一个路径,终点Stage有结果后触发完成,传递结果到B,B在传递结果A,最终结果到达起点。结果在中间传递的过程中在每个Stage都有可能发生变化(取决于每个Stage的逻辑)。
这是一条相对复杂的链路, C的完成单独有终点1触发,C完成后触发A,A将结果推到起点1、起点2;B的完成则必须C和终点2两个Stage都有结果,C和终点2将各自结果推给B,B经过处理再把结果推到终点3。
CompletionStage可以构造更复杂的传递链。利用这个机制,我们可以将系统非阻塞化,尤其适合网关系统,试想一下,网关接收到请求构造一条结果传递链,将请求转发出去后立即释放线程,监听socket数据返回,socket有数据返回后组装结果,开始将结果沿着传递链往起点推。起点将结果写到前端的socket上数据返回。整个过程网关是没有被请求阻塞住的。
CompletionStage提供另多种形态的方法来构建结果传递的链条,并且用方法名来区分:apply、accept、run、handle等等。方法名上带Async说明该动作异步执行(线程池可传,也可用默认线程池)。
方法 | 说明 | 触发条件 |
| 当前Stage正常结束后触发Function执行,当前Stage的结果是Function的参数,并返回一个新的CompletionStage,新CompletionStage的结果是Function的结果或者异常 | 当前Stage完成,只有正常结束才执行动作 |
| 当前Stage正常结束后触发Consumer执行,当前Stage的结果是Consumer的参数,并返回一个新的CompletionStage,新CompletionStage的结果是java.util.concurrent.CompletableFuture#NIL(null)或异常 | 当前Stage完成,只有正常结束才执行动作 |
| 当前Stage正常结束后触发Runnable执行,Runnable是没有参数的(跟thenAccept的区别),并返回一个新的CompletionStage,新CompletionStage的结果是NIL(null)或异常 | 当前Stage完成,只有正常结束才执行动作 |
| 当前Stage以及"other"Stage都正常结束后触发BiFunction执行,当前Stage和"other"Stage的结果作为BiFunction的参数,并返回一个新的CompletionStage,新CompletionStage的结果是BiFunction的结果或者异常 | 当前Stage和"other"Stage完成,只有正常结束才执行动作 |
| 当前Stage以及"other"Stage都正常结束后触发BiConsumer执行,当前Stage和"other"Stage的结果作为BiConsumer的参数,并返回一个新的CompletionStage,新CompletionStage是NIL(null)或异常 | 当前Stage和"other"Stage完成,只有正常结束才执行动作 |
| 当前Stage以及"other"Stage都正常结束后触发Runnalbe执行,Runnable没有参数,并返回一个新的CompletionStage,新CompletionStage的data对象类型Void | 当前Stage和"other"Stage完成,只有正常结束才执行动作 |
| 当前Stage或"other"Stage正常结束后触发Function执行,当前Stage或"other"Stage的结果作为Function的参数(所以"other"Stage的类型必须是当前Stage的子类),并返回一个新的CompletionStage,新CompletionStage的data对象类型是可以由Fucntion改变 | 当前Stage或"other"Stage完成,只有正常结束才执行动作 |
| 当前Stage或"other"Stage正常结束后触发Consumer执行,当前Stage或"other"Stage的结果作为Consumer的参数(所以"other"Stage的类型必须是当前Stage的子类),并返回一个新的CompletionStage,新CompletionStage的结果是NIL(null)或异常 | 当前Stage或"other"Stage完成,只有正常结束才执行动作 |
| 当前Stage或"other"Stage正常结束后触发Runnable执行,当前Stage或"other"Stage的结果作为Runnable的参数(所以"other"Stage的类型无关),并返回一个新的CompletionStage,新CompletionStage的结果是NIL(null)或异常 | 当前Stage或"other"Stage完成,只有正常结束才执行动作 |
| 当前Stage正常结束后触发Function执行,当前Stage结果作为Function的参数,并返回一个新的CompletionStage(称为最终Stage)。跟之前不一样的是Function返回的也是一个CompletionStage(中间Stage),只有中间Stage完成才会使最终Stage完成,最终Stage的结果是中间Stage的结果或异常 | 2阶段触发: 第一阶段:当前Stage完成,只有正常结束触发Function执行并返回中间Stage 第二阶段:中间Stage完成触发最终Stage完成 |
| 当前Stage异常结束后触发Function执行,当前Stage的异常是Function的参数,并返回一个新的CompletionStage,新CompletionStage的结果是Function的结果或者当前Stage结果(正常结束)或者异常(Functioin抛出来) | 当前Stage完成,只有异常结束才执行动作 |
| 当前Stage结束(正常/异常)后触发BiConsumer执行,当前Stage的result/excption是BiConsumer的参数,并返回一个新的CompletionStage,新CompletionStage的result和exception就是当前Stage的result和exception | 当前Stage完成,正常/异常结束都会执行动作 |
| 当前Stage结束(正常/异常)后触发BiFunction执行,当前Stage的result/excption是BiConsumer的参数,并返回一个新的CompletionStage,BiFunction的结果作为新Stage的结果,与whenComplete区别是handle可以通过BiFunction吃调异常 | 当前Stage完成,正常/异常结束都会执行动作 |
CompletableFuture
CompletionStage是一个接口,定义了规范机制,并没有实现,也没有实现构造传递链。CompletableFuture就是做这些事情,实现了CompletionStage的接口规范。
CompletableFuture传递链实现
每个CompletableFuture对象都有一个java.util.concurrent.CompletableFuture.Completion链表,Completion对象包装了目的CompletableFuture对象。当某个CompletableFuture对象完成依次触发Completion链中Completion对象,Completion对象判断是否满足条件(有可能依赖多个CompletableFuture),满足则执行动作,并且把结果赋给自己包装的目的CompletableFuture对象,使目的CompletableFuture对象的完成。目的CompletableFuture对象的完成又会遍历自己的Completion链。
Completion
abstract static class Completion extends ForkJoinTask<Void>
implements Runnable, AsynchronousCompletionTask {
volatile Completion next; // Treiber stack link
/**
* Performs completion action if triggered, returning a
* dependent that may need propagation, if one exists.
*
* @param mode SYNC, ASYNC, or NESTED
*/
abstract CompletableFuture<?> tryFire(int mode);
/** Returns true if possibly still triggerable. Used by cleanStack. */
abstract boolean isLive();
public final void run() { tryFire(ASYNC); }
public final boolean exec() { tryFire(ASYNC); return true; }
}
next:指向下一个Completion形成一个Completion链,注意一个Completion链都是直接依赖当前CompletableFuture的,每个Completion对象包装了目的CompletableFuture,它也有自己的Completion链
- tryFire:尝试判断Completion是否满足条件,满足则执行相关动作,并且将结果传递给目的CompletableFuture,tryFire支持3中模式
-
NESTED(-1):嵌套模式-外部条件促使CompletableFuture对象完成(complete/completeExceptionally/obtrudeValue/obtrudeExceptionlly),这些方法会调用postComplete方法,该方法以嵌套模式触发当前CompletableFuture的Completion链的执行,嵌套模式的特点就是当一个Completion满足条件赋予目的CompletableFuture结果后不会调用执行目的CompletableFuture的postComplete,而是将目的CompletableFuture的Completion链挂在当前对象的Completion链上
- ASYNC(1):异步模式-如果当前Completion是一个异步Completion(有线程池,构建传递链的时候调用的是CompletionStage的Async方法),那么当Completion条件满足的时候,会把Completion丢给线程池调度,线程池调度后以ASYNC模式调用tryFire,异步模式的特点是在执行动作的时候不许要再对Completion宣称主权(java.util.concurrent.CompletableFuture.UniCompletion#claim)了,因为在把Completion丢给线程池之前已经宣称(java.util.concurrent.CompletableFuture.UniCompletion#claim)过一次确保一个Completion只会扔给线程池1次
- SYNC(0):同步模式-是调用CompletionStage的方法构建传递链时,有并发问题,主动尝试一次是否满足条件。同步模式的特点就是没有特点,既要执行目标CompletableFuture的postComplete又要宣称主权
-
- isLive:Completion是否活跃的,判断条件一般就是目的CompletableFuture是否为空(执行过动作就设置为空),如果不是活跃的会从Completion链中移除
- run/exec:都是以异步模式执行tryFire
关键源码(以thenApply为例)
public <U> CompletableFuture<U> thenApply(
Function<? super T,? extends U> fn) {
return uniApplyStage(null, fn);
}
首先是创建一个新的CompletableFuture,尝试去执行一下uniApply(因为如果当前对象已经完成就可以直接执行动作并设置新对象的结果),如果执行结果为false(即当前对象未完成),则创建UniApply(Completion的子类)并且push到当前对象的Completion链上,最后再次尝试Completion能否执行。如果是异步的化就直接创建Completion挂到链子上。
private <V> CompletableFuture<V> uniApplyStage(
Executor e, Function<? super T,? extends V> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<V> d = new CompletableFuture<V>();
if (e != null || !d.uniApply(this, f, null)) {
UniApply<T,V> c = new UniApply<T,V>(e, d, this, f);
push(c);
c.tryFire(SYNC);
}
return d;
}
uniApply方法关键的部分就是在执行动作前先去宣称主权(Async模式参数c为空,不用宣称主权),如果已经被别的线程宣称过则直接返回,放弃后面的执行(因为别的线程会执行),如果宣称到主权但是是异步的Completion则也直接返回,同时将Completion提交给线程池(提交线程池在宣称主权之后所以只会提交一次,宣称主权其实就是一把cas机制的锁)。
final <S> boolean uniApply(CompletableFuture<S> a,
Function<? super S,? extends T> f,
UniApply<S,T> 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 {
//笔者注:如果是以Sync或者Nested模式执行的,c不为空需要宣称主权,确保只有一个线程执行当前Completion,
//如果是Async模式进来的则c为空,因为Async只能由Sync或Nested在宣称主权的时候触发,即Async之前已经宣称过主权已经确保线程池只会调度一次。
if (c != null && !c.claim())
return false;
@SuppressWarnings("unchecked") S s = (S) r;
completeValue(f.apply(s));
} catch (Throwable ex) {
completeThrowable(ex);
}
}
return true;
}
UniApply的tryFire就是执行目的CompletableFuture的uniApply,满足条件就触发目的CompletableFuture的postFire
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) {
CompletableFuture<V> d; CompletableFuture<T> a;
if ((d = dep) == null ||
!d.uniApply(a = src, fn, mode > 0 ? null : this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
postFire方法主要是清理源CompletableFuture的Completion链,移除已经执行过的Completion(根据isLive判断)。最后如果是Nested模式则只需要返回目的CompletableFuture,因为postComplete方法会将目的CompletableFuture的Completion链挂在当前的链上;如果是其它模式则需要触发目的CompletableFuture的postComplete。
final CompletableFuture<T> postFire(CompletableFuture<?> a, int mode) {
if (a != null && a.stack != null) {
if (mode < 0 || a.result == null)
a.cleanStack();
//笔者注:感觉这里多余,因为a是源CompletableFuture,不管a以什么方式设置完成,它上面的Completion链都会被触发,为啥还需要a的目的CompletableFuture来触发,没理解
else
a.postComplete();
}
if (result != null && stack != null) {
if (mode < 0)
return this;
else
postComplete();
}
return null;
}
complete/completeExceptionally
可以看到这两个方法就是给CompletableFuture赋予结果,然后调用postComplete触发Completion链的执行。赋予结果采用的是CAS机制
public boolean complete(T value) {
boolean triggered = completeValue(value);
postComplete();
return triggered;
}
/**
* If not already completed, causes invocations of {@link #get()}
* and related methods to throw the given exception.
*
* @param ex the exception
* @return {@code true} if this invocation caused this CompletableFuture
* to transition to a completed state, else {@code false}
*/
public boolean completeExceptionally(Throwable ex) {
if (ex == null) throw new NullPointerException();
boolean triggered = internalComplete(new AltResult(ex));
postComplete();
return triggered;
}
final boolean internalComplete(Object r) { // CAS from null to r
return UNSAFE.compareAndSwapObject(this, RESULT, null, r);
}
final boolean completeValue(T t) {
return UNSAFE.compareAndSwapObject(this, RESULT, null,
(t == null) ? NIL : t);
}
obtrudeValue/obtrudeExceptionlly
这两个方法跟complete/completeExceptionlly的区别就是比较粗鲁,不管当前result是否有值,直接覆盖。
public void obtrudeValue(T value) {
result = (value == null) ? NIL : value;
postComplete();
}
public void obtrudeException(Throwable ex) {
if (ex == null) throw new NullPointerException();
result = new AltResult(ex);
postComplete();
}
anyOf以及allof
CompletableFuture还提供两个静态方法anyof和allOf。参数都是CompletableFuture数组,这两个方法都返回一个新的CompletableFuture。区别在于anyOf只有参数数组中有一个CompletableFuture对象A完成,则anyOf返回的新对象完成且新对象的结果就是那个对象A的结果;而allOf是要求数组中所有CompletableFuture完成,新对象才完成,而且新对象的结果是NIL或者异常。anyOf和allOf实现机制都是一个倒着的二叉树。
小栗子
没时间写了,后面再补充吧。快写吐血了。。。