11

前言

近期作者对响应式编程越发感兴趣,在内部分享"JAVA9-12"新特性过程中,有两处特性让作者深感兴趣:
1.JAVA9中的JEP266对并发编程工具的更新,包含发布订阅框架Flow和CompletableFuture加强,其中发布订阅框架以java.base模块下的java.util.concurrent.Flow及其中的几个内部类/接口为组成部分,它们的名称和作用如下,摘自JAVA12的Flow api文档。

clipboard.png
2.JAVA9中孵化,JAVA11中标准化的HttpClient,在之前分享的JAVA9-12新特性一文中曾引用摘自网络的HttpClient代码片段:
片段1:

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create(uri))
    .build();

return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
    .thenApply(HttpResponse::body);
}

片段2:

HttpClient client = HttpClient.newHttpClient();
List<String> urls = List.of("http://www.baidu.com","http://www.alibaba.com/","http://www.tencent.com");
List<HttpRequest> requests = urls.stream()
    .map(url -> HttpRequest.newBuilder(URI.create(url)))
    .map(reqBuilder -> reqBuilder.build())
    .collect(Collectors.toList());

List<CompletableFuture<HttpResponse<String>>> futures = requests.stream()
    .map(request -> client.sendAsync(request, HttpResponse.BodyHandlers.ofString()))
    .collect(Collectors.toList());
futures.stream()
    .forEach(e -> e.whenComplete((resp,err) -> {
        if(err != null){
            err.printStackTrace();
        }else{
            System.out.println(resp.body());
            System.out.println(resp.statusCode());
        }
    }));
CompletableFuture.allOf(futures
    .toArray(CompletableFuture<?>[]::new))
    .join();
}

在片段1中,thenApply方法是CompletableFuture的成员,client.sendAsync返回的是一个CompletableFuture。这两段代码很好阅读,甚至说猜出其中的意义。片段2可以说对于作者目前的书写习惯是一个全面的颠覆,显然我们可以预先定义响应行为,而行为的执行时间则由前一个阶段的实际完成时间决定。片段2中的whenComplete方法很好理解,最后一行用allOf生成一个类似树的依赖结构,在当前方法中等待所有CompletableFuture执行完成。

简单看这两段代码,响应式编程的魅力可见一斑,甚至可以说是美妙不可言。
那么,作为JAVA9中额外照顾增强,HttpClient赖以实现的CompletableFuture,它是何方神圣呢?

CompletionStage接口

CompletionStage是什么?不妨卖个关子先。
作者目前使用的JDK版本为8,尽管它不包含9之后的增强,万幸CompletionStage是从JDK8引入,因此足以用以了解这一伟大神器了。近期作者在公司使用的一些开源框架中,发现至处间接对它的使用:
1.持久化框架Redission。它内部使用一个RedissonExecutorService(实现ScheduledExecutorService)和PromiseDelegator(实现CompletionStage,而CompletableFuture同样也实现了CompletionStage)来异步地执行task。
2.apollo配置中心。它提供了配置变更的异步通知机制,而这依赖于spring web-mvc提供的DeferredResult,而在异步处理return value时,DeferredResult的setResult同样也是相应的CompletionStage执行。

//例:阿波罗NotificationControllerV2拉取通知接口
@GetMapping
public DeferredResult<ResponseEntity<List<ApolloConfigNotification>>>     pollNotification(
  @RequestParam(value = "appId") String appId,
  @RequestParam(value = "cluster") String cluster,
  @RequestParam(value = "notifications") String notificationsAsString,
  @RequestParam(value = "dataCenter", required = false) String dataCenter,
  @RequestParam(value = "ip", required = false) String clientIp) {
List<ApolloConfigNotification> notifications = null;
//省略无关代码
//DeferredResultWrapper是apollo作者包装的spring DeferredResult
DeferredResultWrapper deferredResultWrapper = new DeferredResultWrapper();
//省略无关代码
if (!CollectionUtils.isEmpty(newNotifications)) {
  deferredResultWrapper.setResult(newNotifications);
} else {
  deferredResultWrapper
      .onTimeout(() -> logWatchedKeys(watchedKeys, "Apollo.LongPoll.TimeOutKeys"));
 
  deferredResultWrapper.onCompletion(() -> {
    //unregister all keys
    for (String key : watchedKeys) {
      deferredResults.remove(key, deferredResultWrapper);
    }
    logWatchedKeys(watchedKeys, "Apollo.LongPoll.CompletedKeys");
  });
 
  //省略
return deferredResultWrapper.getResult();
}

在spring的CompletionStageReturnValueHandler的handleReturnValue()方法中,如下异步地处理响应结果:

@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
 
    if (returnValue == null) {
        mavContainer.setRequestHandled(true);
        return;
    }
 
    final DeferredResult<Object> deferredResult = new DeferredResult<Object>();
    WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(deferredResult, mavContainer);
 
    @SuppressWarnings("unchecked")
    CompletionStage<Object> future = (CompletionStage<Object>) returnValue;
    future.thenAccept(new Consumer<Object>() {
        @Override
        public void accept(Object result) {
            //在一个CompletionStage完成后执行此方法,为defferedResult设值
            deferredResult.setResult(result);
        }
    });
    future.exceptionally(new Function<Throwable, Object>() {
        @Override
        public Object apply(Throwable ex) {
            //在一个CompletionStage执行出错后执行此方法,为deferredResult设值
            deferredResult.setErrorResult(ex);
            return null;
        }
    });
}

以上代码的future.thenAccept与future.exceptionally只是规定了两种情况下程序接下来的运行行为,相应的代码不是立即执行,而是等到相应的行为发生了才去执行。很明显,同步式编程写流程,响应式编程似乎就是在写行为。
显然,只要熟悉了CompletionStage的api,以上的代码就绝对简单了,好了,开胃菜已上完,接下来介绍CompletionStage。
CompletionStage其实很好理解,按照官方定义,它表示一个可能异步运行的“阶段”,在该阶段内要执行相应的行为,而这些运算会在另一个CompletionStage完成后开始,它自身完成后又可触发另一个依赖的CompletionStage。

clipboard.png

在CompletionStage中这些方法均可用来定义一个行为,行为的执行方式可参考方法名和入参,这与java8中的stream api持同样的风格。行为参数可以是Consumer,Function,Runnable。包含accept的方法,参数会有一个Consumer,它会消费上一或多个CompletionStage的结果;包含run的方法,参数会有一个Runnable,它的运行不需要前面CompletionStage的执行结果;包含apply的方法,参数会包含Function,该function一般以前一或几阶段的返回值为入参,以自身的执行结果作为当前CompletionStage的结果。
CompletionStage和实现类ComletableFuture的方法名中也会包含either/all/any等简单的单词,和上述的含义相组合,不难理解。
以以下三个接口为例说明:

1.public CompletionStage<Void> runAfterBoth(CompletionStage<?> other,Runnable action);

接口会返回一个CompletionStage,该stage仅在当前stage和参数中的other正常完成后才会执行参数中的action。

2.public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn);

接口会返回一个CompletionStage,该stage会在当前stage或参数中的other正常执行完毕后异步执行参数中的函数fn,而fn的参数就是前面执行完毕的stage的结果,fn的返回值将是被返回的stage的结果。

3.public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor);

接口会返回一个CompletionStage,它会在当前stage和参数中的other正常执行完毕后执行,以这两个stage的结果作为参数,在参数executor线程池中执行action函数,因为它是一个消费者,因此没有返回值。
接口的其他方法逻辑类似,不再缀述。

CompletableFuture源码

上一节简述了CompletionStage接口的函数定义,作为官方提供的实现类,CompletableFuture实现了有关的所有接口,它的作者依旧是我等膜拜的道格大神,下面来具体分析CompletableFuture的实现。

类签名:

public class CompletableFuture<T> implements Future<T>, CompletionStage<T>
从签名信息来看,CompletableFuture实现了Future和CompletionStage接口,这意味着它即满足CompletableStage的阶段执行,也提供了Future中获取该执行结果的方法。

首先来看成员变量和核心函数:

volatile Object result;       // 当前对象的结果或者一个异常包装对象AltResult,关于AltResult下面再看
volatile Completion stack;    // 任务栈,Completion后面再述。
 
final boolean internalComplete(Object r) {
//使用cas原子操作,将原本为null的result置为r,所有调用者都保证r不是null,因此只有第一次才能返回true。
    return UNSAFE.compareAndSwapObject(this, RESULT, null, r);
}
 
final boolean casStack(Completion cmp, Completion val) {
//尝试用cas原子操作将当前stack的值从cmp换为val。
    return UNSAFE.compareAndSwapObject(this, STACK, cmp, val);
}
//其中STACK,RESULT就是上面stack和result的句柄,这点和其他juc中的工具惯例相同
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);
    }
}

stack的类型为Completion,为了方便理解,在介绍Completion类之前,先看几个声明在CompletableFuture的常量

static final int SYNC   =  0;//同步
static final int ASYNC  =  1;//异步
static final int NESTED = -1;//嵌套

再来看Completion类的结构

//继承ForkJoinTask,实现Runnable,以及签名接口AsynchronousCompletionTask
abstract static class Completion extends ForkJoinTask<Void>  implements Runnable, AsynchronousCompletionTask {
    volatile Completion next;      // 指向下一个Completion
 
    //当被触发时,执行completion动作,如果存在需要传递的行为,
    //返回一个代表该行为的CompletableFuture  
    //参数只能是上面提到的SYNC,ASYNC,NESTED,后面留意它的正负。  
    abstract CompletableFuture<?> tryFire(int mode);
 
    //如果当前completion依旧是可触发的,则返回true,这会在清理任务栈时使用. 
    abstract boolean isLive();
    //继承自Runnable,直接调用tryFile,参数为1
    public final void run() { tryFire(ASYNC); }
    //继承自ForkJoinTask,直接调用tryFile,参数为1,返回true
    public final boolean exec() { tryFire(ASYNC); return true; }
    //继承自ForkJoinTask,直接返回null
    public final Void getRawResult() { return null; }
    //继承自ForkJoinTask,空方法。
    public final void setRawResult(Void v) {}
}

上面列举了内部类Completion的全部代码,它继承并实现了ForkJoinTask和Runnable中的抽象方法,同时声明了tryFire这个抽象方法供子类实现。因为继承了ForkJoinTask,这意味着Completion也是一个任务,且它可能在ForkJoinPool中执行。关于Completion和它的子类后面详述。先来继续看核心函数和成员实现。

/** 尝试将一个任务压栈,成功返回true */
final boolean tryPushStack(Completion c) {
    Completion h = stack;
    lazySetNext(c, h);//把当前的栈设置为c的next
    //尝试把当前栈(h)更新为新值(c)
    return UNSAFE.compareAndSwapObject(this, STACK, h, c);
}
//lazySetNext定义
static void lazySetNext(Completion c, Completion next) {
    UNSAFE.putOrderedObject(c, NEXT, next);
}

方法tryPushStack的流程很简单,先调用lazySetNext将当前栈设置为参数的next,这样达到了栈的后入为顶层的目的,然后试图将顶部元素设置为新压入栈的c。

/** 不加锁将任务压栈,使用cas加自旋的方式,这也是道格大神的经典. */
final void pushStack(Completion c) {
    do {} while (!tryPushStack(c));
}

接下来是一些对输出结果编码的代码。

//内部类,用于对null和异常进行包装,从而保证对result进行cas只有一次成功。
static final class AltResult { // See above
    final Throwable ex;        // null only for NIL
    AltResult(Throwable x) { this.ex = x; }
}
 
/** 空值用一个ex为null的AltResult表示 */
static final AltResult NIL = new AltResult(null);
 
/** 使用上面的NIL完成任务,若任务已经被完成过,返回false */
final boolean completeNull() {
    return UNSAFE.compareAndSwapObject(this, RESULT, null,
                                       NIL);
}
 
/** 对空值进行编码,使用NIL */
final Object encodeValue(T t) {
    return (t == null) ? NIL : t;
}
 
/** 使用t完成当前任务,t是null时使用NIL作为结果,否则使用t */
final boolean completeValue(T t) {
    return UNSAFE.compareAndSwapObject(this, RESULT, null,
                                       (t == null) ? NIL : t);
}
 
//对异常进行编码,返回一个AltResult,其值ex取决于参数x,
//若x为CompletionException则直接用x赋值ex,
//否则用CoimpletionException包一层。 
static AltResult encodeThrowable(Throwable x) {
    return new AltResult((x instanceof CompletionException) ? x :
                         new CompletionException(x));
}
 
/** 使用参数提供的异常的编码结果完成任务,若result已非空,返回false */
final boolean completeThrowable(Throwable x) {
    return UNSAFE.compareAndSwapObject(this, RESULT, null,
                                       encodeThrowable(x));
}
 
// 如果x非CompletionException,将它包裹成CompletionException返回。
//如果不是,则判断,若r是AltResult且其ex就是参数x的值,则将r返回。  
// 否则将x包裹成AltResult返回。
static Object encodeThrowable(Throwable x, Object r) {
    if (!(x instanceof CompletionException))
        x = new CompletionException(x);
    else if (r instanceof AltResult && x == ((AltResult)r).ex)
        return r;
    return new AltResult(x);
}
 
// 给定一个Throwble x,一个Object r,使用上面的方法编码的结果来尝试完成。  
final boolean completeThrowable(Throwable x, Object r) {
    return UNSAFE.compareAndSwapObject(this, RESULT, null,
                                       encodeThrowable(x, r));
}
 
//如果x不是null,使用上面的encodeThrowable对x编码的结果返回,否则若t是空,  
// 返回NIL,否则返回t。
Object encodeOutcome(T t, Throwable x) {
    return (x == null) ? (t == null) ? NIL : t : encodeThrowable(x);
}
 
 
static Object encodeRelay(Object r) {
    Throwable x;
    //对非空参数r进行判断。
    //若r是AltResult且具备非空的ex,且ex并不是CompletionException类型,
    //将ex包装成CompletionException,并包裹成AltResult返回。
    //其他情况直接返回r。
    return (((r instanceof AltResult) &&
             (x = ((AltResult)r).ex) != null &&
             !(x instanceof CompletionException)) ?
            new AltResult(new CompletionException(x)) : r);
}
 
 
final boolean completeRelay(Object r) {
//这段代码的逻辑和上一个方法联合去看,当前未完成的情况下,尝试使用参数r完成。
//如果r是异常,尝试将它包装成CompletionException并外包一层AltResult。
//用这个AltResult完成。
    return UNSAFE.compareAndSwapObject(this, RESULT, null,
                                       encodeRelay(r));
}

CompletableFuture本质也是一个Future,因此也会支持异步的阻塞的result获取。因为在完成这个future时,为了便于处理和维护,使用了编码的结果,固在读取结果时,也要对结果进行解码。

/**  * 供future.get()使用。  */
private static <T> T reportGet(Object r)
    throws InterruptedException, ExecutionException {
    if (r == null)
    //参数r代表一个CompletableFuture的result,因为它会对异常和null进行编码。
    //故null可以视为get的中间被扰动的结果。
        throw new InterruptedException();
    if (r instanceof AltResult) {
        Throwable x, cause;
        //这一段很简单,是AltResult,ex是空返回空。
        if ((x = ((AltResult)r).ex) == null)
            return null;
             
        if (x instanceof CancellationException)
        //ex是取消异常,转换后抛出。
            throw (CancellationException)x;
        if ((x instanceof CompletionException) &&
            (cause = x.getCause()) != null)
            //异常是包装异常CompletionException,取出被包装的异常抛出。
            x = cause;
        throw new ExecutionException(x);
    }
    //result不是null也不能体现异常,强转返回。
    @SuppressWarnings("unchecked") T t = (T) r;
    return t;
}
 
//reportJoin方法相对简单,因为join操作会一直等待,r能保证非空。  
//对于非AltResult类型的r直接强转返回,AltResult类型的处理与  
//reportGet类似,但是不解CompletionException,直接抛出。  
//此方法抛出的异常均不受检。 
private static <T> T reportJoin(Object r) {
    if (r instanceof AltResult) {
        Throwable x;
        if ((x = ((AltResult)r).ex) == null)
            return null;
        if (x instanceof CancellationException)
            throw (CancellationException)x;
        if (x instanceof CompletionException)
            throw (CompletionException)x;
        throw new CompletionException(x);
    }
    @SuppressWarnings("unchecked") T t = (T) r;
    return t;
}

相应的get和join方法实现。

public T get() throws InterruptedException, ExecutionException {
    Object r;
    return reportGet((r = result) == null ? waitingGet(true) : r);
}
public T join() {
    Object r;
    return reportJoin((r = result) == null ? waitingGet(false) : r);
}

可以看出,get和join方法分别先调用reportGet,reportJoin,若得到的空结果,会继续调用waitingGet方法,只是参数分别为true和false,waitingGet方法的实现需要先了解剩余的核心函数以及Completion子类,稍后再看。

一些与异步操作的准备:

/**  * 标识是异步方法产生的任务的接口,对于异步行为的监控,debug,追踪会很有用。  
*     在jdk8的CompletableFuture实现中,它有三个直接实现类,AsyncRun,  
*     AsyncSupply以及前面提到过的Completion。  
*/
public static interface AsynchronousCompletionTask {
}
//判断是否使用ForkJoinPool的common线程池,在ForkJoinTask中持有该线程池的引用。
//判断规则是可用cpu核数大于1.
private static final boolean useCommonPool =
    (ForkJoinPool.getCommonPoolParallelism() > 1);
 
//异步线程池,根据上述判断,决定使用commonPool还是ThreadPerTaskExecutor,  
// 后者是一个对每一个任务都新建一个线程的low逼线程池。 
private static final Executor asyncPool = useCommonPool ?
    ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
 
/** low逼线程池源码,没什么可说的 */
static final class ThreadPerTaskExecutor implements Executor {
    public void execute(Runnable r) { new Thread(r).start(); }
}
 
 
static Executor screenExecutor(Executor e) {
    if (!useCommonPool && e == ForkJoinPool.commonPool())
    //判断参数执行器(线程池的父接口,一般会传入线程池)是否需要屏蔽,
    //如果参数就是ForkJoinPool.commonPool()并且经前面的系统判断
    //useCommonPool为false,则强制使用asyncPool。
        return asyncPool;
    if (e == null) throw new NullPointerException();
//非空且通过验证,返回参数e
return e;

}

为异步做的这些准备很好理解,屏蔽不合理的线程池使用,在用户提供的线程池,commonPool和ThreadPerTaskExecutor之中择一,在后续的操作中需要使用它们。
还有两个重要的核心函数,是道格大神的神作。

final void postComplete() {
     
    CompletableFuture<?> f = this; Completion h;//初始化f为this
    while ((h = f.stack) != null ||//1,f的栈非空
           (f != this && (h = (f = this).stack) != null)) {//2 f的栈为空且不是this,重置
        CompletableFuture<?> d; Completion t;
        if (f.casStack(h, t = h.next)) {//3 h出栈
            if (t != null) {//4 出栈的h不是最后一个元素,最后一个元素直接执行7即可,减少一次循环cas竞态
                if (f != this) {//f不是this
                    pushStack(h);//5 将f刚出栈的h(顶)入this的栈(顶)
                    continue;
                }
                h.next = null;    //6 detach 帮助gc
            }
            //tryFire参数为NESTED,即-1,这是它唯一一次使用。
            f = (d = h.tryFire(NESTED)) == null ? this : d;//7 f栈的最后一个元素或者就是this栈中的元素
        }
    }
}

这寥寥数行代码的含金量不可小觑。它应在将要完成时调用,很明显,它会将当前CompletableFuture的栈以及传递依赖的其他CompletableFuture的栈清空。为了便于解释,在相应的代码上打出了编号,下面详细分析。

调用该方法,首先进入1,此时f是当前CompletableFuture,h是它的stack,满足不为空的判断,进入3.

到达3时,将栈顶Completion h出栈,一般除非并发多个线程对同一个CompletableFuture调用postComplete,否则一定会成功并到达4。若出现多个线程调用,cas失败,则重新循环。

到达4后,若发现f的栈已空,则直接进入7,否则判断f是否为当前CompletableFuture,若是,则进行6,取消h和t的关联,若不是则进入5,将h(f中刚刚移除的栈顶)压入当前Completable的栈并重新循环。

显然,只要处理当前CompletableFuture的栈,就一定会执行7,只要处理的是另一个CompletableFuture的栈,就会将其出栈,然后压入当前CompletableFuture的栈。

在7处,会尝试执行栈顶的Completion的tryFile方法,它会返回一个可能为null的CompletableFuture,若非空,则赋给f,否则将this赋给f。

所以这段方法的真实执行流程:当前CompletableFuture的栈中元素逐个出栈并tryFile,发现新的CompletableFuture,将它的元素反向压入本CompletableFuture的栈,压入结束后,继续对栈中元素逐个出栈并tryFire,发现非空CompletableFuture则继续上述过程。直到本CompletableFuture的栈中不再有元素(此时tryFire返回的CompletableFuture栈也是空的)为止。

膜拜道格大神的同时,顺便点一下,这似乎是一种避免递归的方式。只不过tryFire返回的CompletableFuture中的栈元素将会反向执行。

/* 遍历栈并去除死亡任务/

final void cleanStack() {
    for (Completion p = null, q = stack; q != null;) {//初始条件,q指向null时终止。
        Completion s = q.next;//循环内第一行,q永远指向栈顶,s永远指向栈顶第二个元素或者null
        if (q.isLive()) {//a只要q存活,就将p指向q,并将q指向s
            p = q;
            q = s;
        }
        else if (p == null) {//b q不存活,p是null,两种可能,从未见到存活的节点,或执行过最后的重启
            casStack(q, s);/将q出栈
            q = stack;//变量q重新指向新的栈顶。
        }
        else {
            p.next = s;//q已死亡,且当前已经找到过存活的元素。p指向q的下一个元素s,从而将q出栈
            if (p.isLive())//c判断p是否存活,而p只能是null或者最近一个存活的Completion
                q = s;//6.q前进
            else {//4
                p = null;  //d 重新将p置null并将q指向当前的栈,重启循环。
                q = stack;
            }
        }
    }
}

为了让这段代码的说明更加清晰,不妨举个简单的例子说明。

假定当前CompletableFuture的栈中有1-9个元素,其中14568在调用cleanStack方法时已死亡,在执行过程中,也出现方法执行过程中出现死亡的状态。
进入循环,p为null,q指向1,满足循环条件,开始第一轮循环。
第一轮循环进入后,s指向2,p为null,q指向1,是个死亡对象,因此在第一个判断条件a处未能通过,b判断条件为真,q被移除,循环结束,此时p为null,q指向2,栈变为2-9.
第二轮循环进入,s指向3,p为null,q指向2,是个存活对象,进入a,循环结束,p指向2,q指向3。栈依旧为2-9.
第三轮循环进入,s指向4,p为2,q指向3,是存活对象,进入a,循环结束,p指向3,q指向4,栈保持2-9不变。
第四轮循环进入,s指向5,p为3,q指向4,是个死亡对象,p非空且存活,进入c,则p保持为3,3的next指向5,q指向5.循环结束,栈变为2356789.
第五轮循环进入,s指向6,p指向3,q指向5,是个死亡对象,p非空且存活,进入c,p保持为3,3的next指向6,q指向6,循环结束,栈变为236789.
第六轮循环进入,s指向7,p指向3,q指向6,是个死亡对象,假定此时3死亡,则3的next指向7,进入d分支,p为null,q为2,栈为23789.
第七轮循环进入,s指向3,p为null,q指向2,是个存活对象,p指向2,q指向3,栈依旧为23789.
第八轮循环进入,s指向4,p指向2,q指向3,是个死亡对象,p非空且存活,进入c,则p保持为2,q指向7,3的next指向7,栈变2789.
第九轮进入,s指向8,p指向2,q指向7,是个存活对象,进入a分支,p变为7,q变为8,栈保持2789.假定此步之后2死亡,但此时p已经指向7.
第十轮进入,s指向9,p指向7,q指向8,是个死亡对象,p当前指向7且存活,所以尽管2不存活,仍旧进入分支c,p保持为7,q指向9,7的next指向9.栈为279.
第十一轮,s为null,p指向7,q指向9,是个存活对象,则进入a分支,p变为9,q变为null,栈保持279.
因q为null,循环终止。栈经过清理只剩下279三个元素,其中2因为巧合而死亡且未被清理。

下面回到Completion,Completion是一个抽象类,前面已经简单展示它的源码,它的子类如下:

clipboard.png
可以看到有三个直接子类,CoCompletion,Signaller和UniCompletion。UniCompletion又有若干子类,它们分别作为一些CompletionStage中声明方法的实现工具,很明显,道格大神在此处大量使用了策略模式。
先来简单看一下CoCompletion的实现:

static final class CoCompletion extends Completion {
    //CoCompletion完全委托给base执行。
    BiCompletion<?,?,?> base;
    CoCompletion(BiCompletion<?,?,?> base) { this.base = base; }
    final CompletableFuture<?> tryFire(int mode) {
        BiCompletion<?,?,?> c; CompletableFuture<?> d;
        if ((c = base) == null || (d = c.tryFire(mode)) == null)
            //base未指定,或base的tryFire返回null,则返回null。
            return null;
        base = null; // 解除关联,再isLive判断为死亡。
        //返回的d就是base的tryFire返回的非空CompletableFuture
        return d;
    }
    final boolean isLive() {
        BiCompletion<?,?,?> c;
        //存活标准,base非空且base的dep非空。
        return (c = base) != null && c.dep != null;
    }
}

CoCompletion虽然是Completion的直接子类,但它依赖了BiCompletion,且BiCompletion是UniCompletion的直接子类,先来看UniCompletion.

abstract static class UniCompletion<T,V> extends Completion {
    Executor executor;//用来执行任务的执行器                
    CompletableFuture<V> dep; //要完成的依赖CompletableFuture
    CompletableFuture<T> src; //作为行为源的CompletableFuture
 
    UniCompletion(Executor executor, CompletableFuture<V> dep,
                  CompletableFuture<T> src) {
        this.executor = executor; this.dep = dep; this.src = src;
    }
 
     
    final boolean claim() {
        Executor e = executor;
        if (compareAndSetForkJoinTaskTag((short)0, (short)1)) {//1
            //compareAndSetForkJoinTaskTag是ForkJoinTask的方法,利用cas,保证任何一种情况下,该行为只能执行一次。
            if (e == null)
                //不精确的说法是同步调用返回true,异步调用返回false,然后在线程池中执行。
                //有e代表要在执行器中执行,尽管大多数情况下e都是线程池实例,会异步运行任务。但对于Executor来说,完全可以实现在同一个线程执行。
                return true;//2.
            //对于3这行代码,道格大神注释就写了个disable,为此我翻了大量代码,发现根本过不了上面cas这一关,所以个人有两个理解:
            //1.对于当前Completion而言,它的线程池只能用来做一次事,在claim之后立即置空,尽管此时还没有执行action,也不允许当前Completion使用它做别的事了。
            //2.减少了一个指向该线程池的引用,线程池也有被gc的时候吧。就算不gc,关闭虚拟机或者dump的时候也能少做点事。
            executor = null; // 3.
            e.execute(this);//使用该线程池异步执行,回忆上面Completion的声明,它实现了runnable,在run方法中tryFire(ASYNC),参数ASYNC是正数。
        }
        return false;
    }
 
    final boolean isLive() { return dep != null; }
}

尽管UniCompletion本身代码不多,但是有关代码却很绕,后面会从CompletableFuture调用开始说明一个完整的工作流,作者本来有几次都已经十分艰难的“确定发现问题”,写出了“问题”,但最终还是在描述过程中启动大脑自我否定,不得不佩服道格大神强大的逻辑和大脑。

很明显,UniCompletion是一个可以拥有执行器的Completion,它是两个操作的结合,dep为要最终执行的依赖操作,src为来源CompletableFuture,tryFire没有默认实现,它的子类分别根据不同情况实现了该方法,实现的方式依旧是优雅的策略模式。

claim方法要在执行action前调用,若claim方法返回false,则不能调用action,原则上要保证action只执行一次。

claim的意思是声称,开个玩笑,在美剧行尸走肉第四季,有一伙武装分子解决为了解决内部分配问题的提出了一个办法,对任何事物只看谁先喊一句”claimed“,代表”我要了“。调用claim方法和稍后运行action的动作发生在一个线程,因此需要该线程尝试去claim这个action,claim成功则执行,claim不成功则不执行。

但在提供Executor的前提下,claim除了声明以外,还会直接在方法内使用该executor执行tryFire,间接地执行action,并返回false,避免调用者也执行action,因为有cas的效果,多次claim只有第一次可能返回true。

接下来看BiCompletion,它也是一个抽象类,不同在于它有两个源,也就是它的成员dep要等到另外两个成员CompletableFuture(src,snd)完成,具体的依赖关系要看子类实现。

abstract static class BiCompletion<T,U,V> extends UniCompletion<T,V> {
    CompletableFuture<U> snd; // 第二个源action
    BiCompletion(Executor executor, CompletableFuture<V> dep,
                 CompletableFuture<T> src, CompletableFuture<U> snd) {
        super(executor, dep, src); this.snd = snd;
    }
}

BiCompletion有多个实现类,看名称可以看到Apply,Accept,Run等字眼,前面已经讨论过相应的语义。

clipboard.png

以OrApply为例

static final class OrApply<T,U extends T,V> extends BiCompletion<T,U,V> {
    Function<? super T,? extends V> fn;
    OrApply(Executor executor, CompletableFuture<V> dep,
            CompletableFuture<T> src,
            CompletableFuture<U> snd,
            Function<? super T,? extends V> fn) {
        //构造函数,多传一个函数,该函数就是dep对应的action。
        super(executor, dep, src, snd); this.fn = fn;
    }
    //tryFire父类没有实现
    final CompletableFuture<V> tryFire(int mode) {
        CompletableFuture<V> d;
        CompletableFuture<T> a;
        CompletableFuture<U> b;
        if ((d = dep) == null ||//没有dep,则没有相应的依赖行为,已经执行过的dep会是null。
            //执行orApply返回false,则返回null。最后一个参数仅当mode是ASYNC(只有它大于1)时会是this
            !d.orApply(a = src, b = snd, fn, mode > 0 ? null : this))
            //到此可能是运行过,或者不满执行fn的条件,返回null。
            return null;
        //前面dep不是null,执行orApply也成功了,则解除引用关联,下次运行会直接返回null,也不影响gc。
        //回忆前面看过的核心函数postComplete,会对CompletableFuture中栈上的所有Completion进行tryFire,
        //返回非null则进行类似递归的操作,很明显,在调用postComplete
        //方法时,dep为null会返回一个null,避免了再次tryFire。
        dep = null; src = null; snd = null; fn = null;
        //正常运行结束,调用dep的postFire并返回。
        return d.postFire(a, b, mode);
    }
}

orApply方法定义在CompletionFuture。前面没有叙述。它不是作者称为的”核心函数“(即各种Completion都能使用到)。

final <R,S extends R> boolean orApply(CompletableFuture<R> a,
                                      CompletableFuture<S> b,
                                      Function<? super R, ? extends T> f,
                                      OrApply<R,S,T> c) {
    Object r; Throwable x;
    if (a == null || b == null ||
        //为r赋值用于后续的计算,因为是or,r优先取第一个,第一个源action未完成的情况下再取第二个。
        ((r = a.result) == null && (r = b.result) == null) || f == null)
        //首先检测两个源action,若a和b均未完成,则说明依赖dep不可被执行,返回false。
        return false;
    //仅当当前(dep)未完成(result为null)时,可进行完成工作。
    tryComplete: if (result == null) {
        try {
            //前面说过,c不为null说明是异步执行,需要先去尝试claim这个action。
            if (c != null && !c.claim())
                //异步且claim不成功,返回false。
                return false;
            if (r instanceof AltResult) {
                if ((x = ((AltResult)r).ex) != null) {
                    //如果r表示异常,调用completeThrowable核心函数并结束代码块,直接返回true。
                    completeThrowable(x, r);
                    break tryComplete;
                }
                //第一个非空的action(a或b)结果代表异常,但ex是null,则将r置为null并返回true。
                r = null;
            }
            //r不代表异常结果,直接强转,用该结果作为action的参数,执行action,用结果作为当前的result。出现异常则进入catch块。
            @SuppressWarnings("unchecked") R rr = (R) r;
            completeValue(f.apply(rr));
        } catch (Throwable ex) {
            //上述代码出现异常,调用completeThrowable完成dep(this)
            completeThrowable(ex);
        }
    }
    return true;
}

正常运行结束还会调用dep的postFire,它也位于CompletableFuture中,但它只供 BiCompletion在tryFire成功之后才可使用,该方法源码如下:


final CompletableFuture<T> postFire(CompletableFuture<?> a,
                                    CompletableFuture<?> b, int mode) {
    //对于ab两个源,先处理b,后处理a
    if (b != null && b.stack != null) {
        //b存在且b的栈还有元素
        if (mode < 0 || b.result == null)
            //当为NESTED(只有它的值是-1)时,或者b没有结果时,对b进行清栈。调用postFire意味着d执行tryFire成功,
            //即d获得了结果,而这前提是ab之一已执行成功(orApply的含义),所以ab可能是其一完成。
            b.cleanStack();
        else
            //非NESTED,则对b进行postComplete,该方法内部又会对b的栈上的每一个Completion执行tryFire,而且用NESTED模式。
            b.postComplete();
    }
    //接下来对a直接进行postFire,并沿用mode。
    return postFire(a, mode);
}

对a进行postComplete的方法如下:

final CompletableFuture<T> postFire(CompletableFuture<?> a, int mode) {
    if (a != null && a.stack != null) {
        //栈非空
        if (mode < 0 || a.result == null)
        //类似上面的逻辑,是NESTED模式时或者a未完成时,对a进行清栈,否则对a执行postComplete.
            a.cleanStack();
        else
            a.postComplete();
    }
    //处理a之后,处理当前(即dep)
    if (result != null && stack != null) {
        //有结果且栈非空
        if (mode < 0)
            //NESTED模式,直接返回this。
            return this;
        else
            //非NESTED模式,执行postComplete,其中会对d的栈中所有Completion进行tryFire(NESTED),
            //并在每一个tryFire返回的CompletableFuture逆栈执行同一样操作,参见上面的源码。
            postComplete();
    }
    return null;
}

以上是全部与OrApply的实现有关的源码,下面来看一看OrApply的应用,再简单梳理一下流程。

在CompletableFuture中有三个有关的方法:

clipboard.png

可以看到三个方法的签名和调用信息,这三个方法均是实现自CompletionStage。关于方法的字意和大致逻辑的推测方法前面已分析。

public <U> CompletableFuture<U> applyToEither(
    CompletionStage<? extends T> other, Function<? super T, U> fn) {
    //直接调用orApplyStage,不指定线程池。
    return orApplyStage(null, other, fn);
}
 
public <U> CompletableFuture<U> applyToEitherAsync(
    CompletionStage<? extends T> other, Function<? super T, U> fn) {
    //调用orApplyStage方法,外部不提供线程池,使用asyncPool,关于asyncPool前面已分析。
    return orApplyStage(asyncPool, other, fn);
}
 
public <U> CompletableFuture<U> applyToEitherAsync(
    CompletionStage<? extends T> other, Function<? super T, U> fn,
    Executor executor) {
    //调用orApplyStage方法,但对外面传入的线程池进行屏蔽,条件符合则使用,不符合则更换,屏蔽原则前面已分析。
    return orApplyStage(screenExecutor(executor), other, fn);
}

可见三个方法均使用了orApplyStage方法,只是在参数上有所不同。再来看orApplyStage方法。

private <U extends T,V> CompletableFuture<V> orApplyStage(
    Executor e, CompletionStage<U> o,
    Function<? super T, ? extends V> f) {
    CompletableFuture<U> b;
    if (f == null || (b = o.toCompletableFuture()) == null)
        //要执行的函数未提供,或者参数o转换的CompletableFuture也是null,则抛出空指针。
        throw new NullPointerException();
    //新建了一个dep,后面将它返回,故直接调用实现自CompletionStage的方法不用考虑返回空的问题,可以链式调用。
    CompletableFuture<V> d = new CompletableFuture<V>();
    //如果指定了线程池,直接进入if。未指定线程池,首先尝试调用orApply方法,并以this和b作参数。
    //前面分析过,若条件满足,即this和b有一个是完成态,则会立即执行f,结果或异常作为d的结果。
    //d.orApply的最后一个参数是null(c),说明是同步操作,不会进行c.claim操作。
    if (e != null || !d.orApply(this, b, f, null)) {
        //指定了线程池,或者尝试d.orApply条件不满足,转为异步。
        //构建OrApply对象压入Completion栈。
        OrApply<T,U,V> c = new OrApply<T,U,V>(e, d, this, b, f);
        orpush(b, c);
        //压栈后再次尝试同步调用一次tryFire,前面分析过,tryFire成功会最终调用相应的cleanStack,postComplete等操作,
        //将死亡的Completion(各子类有不同的判定,CoCompletion判定base是null,有些判断dep是null,而完成一般会把dep置null)
        //从栈上移除。
        c.tryFire(SYNC);
    }
    return d;
}
 
 
public CompletableFuture<T> toCompletableFuture() {
    //直接返回this
    return this;
}
final void orpush(CompletableFuture<?> b, BiCompletion<?,?,?> c) {
    if (c != null) {
        //循环条件,b不存在或未完成且同时当前CompletableFuture未完成。有任何一个完成则终止,若无完成,则执行下面的代码将任务入this和b的栈。
        while ((b == null || b.result == null) && result == null) {
            //将c压入当前CompletableFuture栈并退出循环。
            if (tryPushStack(c)) {
                if (b != null && b != this && b.result == null) {
                    //存在b,b不是当前,b未完成时。尝试将c封装成CoCompletion并压入b的栈,前面说过
                    //这个压入b栈的q完全依赖于c,并使用c的运行结果。
                    Completion q = new CoCompletion(c);
                    //内循环,参数外循环说明。
                    while (result == null && b.result == null &&
                           !b.tryPushStack(q))
                        lazySetNext(q, null); // clear on failure
                }
                break;
            }
            //到此说明c压入当前栈失败,则将c的next恢复为null。
            lazySetNext(c, null); // clear on failure
        }
    }
}

简单梳理OrApply这一条线的流程,其他线逻辑类似。

当使用Completable的applyToEitherAsync/applyToEither时,将进入这一条线的代码执行,CompletableFuture在初步验参后,会封装一个d用于表示结果的CompletableFuture,稍后将会用它作为返回值。随后根据入参不同而进入不停的逻辑。

同步的情况,即未提供Executor,首先就尝试调用它的d.uniApply方法,若此时当前CompletableFuture或参数中的另一个stage已完成,则用完成的结果直接执行用户指定的action并对d的结果进行赋值,并进一步完成d的后续清栈和postComplete(1);若此时当前的Completable或另一个stage未完成,则不满足执行action的条件,将当前Completable作为第一个source,另一个stage作为第二个source,封装成一个OrApply并压当前CompletableFuture和另一个stage的栈(2),随后立即以同步方式调用它的tryFire(1)。

异步的情况,直接封装OrApply对象,将由线程池间接调用tryFire(3),进一步调用orApply方法,因为是异步,即使满足了前面的条件(ab之一正常或异常完成),依旧需要进行claim,claim失败则不会执行action。claim成功,执行action出现异常,则用异常来完成这个action。

以上三种情况最终都会执行action,标注了(1)和(3)是很明确的两种情况。

任何一个CompletableFuture完成后,都会根据mode进行后续处理,其实尽管每个Completion都具备一个next指针,但每一个Completion的完成均不依赖于栈中的其他Completion,仅在cleanStack,压栈,postComplete使用了该栈的结构。现在来回答前面分析时发现的两个问题。

1.当前CompletableFuture在完成后,执行postComplete,会将它自身的栈中completion出栈并执行action,若要产生新的CompletableFuture,则将它的栈反向压入自身的栈,然后重复执行出栈-执行的操作。反向压栈有问题吗?答案是没有。因为栈中的每一个Completion在执行上互不影响,它们的顺序只影响到cleanStack和postComplete的处理顺序。CompletableFuture和它的栈元素产生的CompletableFuture彼此间有顺序要求,但对同一个CompletableFuture的栈内的Completion元素彼此间没有顺序要求,决定他们顺序的是对源CompletionFuture调用orApply,thenApply等等方法的顺序,后续运行也完全独立。只不过在源CompletableFuture进行postComplete时,执行的顺序将会与原本的”先来后到“相反。

2.cleanStack到一半,p指向的Completion依旧存活,位于p以上的Completion已执行完毕,那么不会重新开始循环,p之前的死亡Completion会留在栈中。这也是为什么前面使用OrApply来解释这个问题的原因,因为很可能就不存在这个问题。根据前面的源码,仅有postComplete触发的tryFire会使用NESTED(-1)模式,只有NESTED模式下,或者源CompletableFuture的result为null(未完成)的情况下执行postFire才会进入到cleanStack,否则会进入postComplete,后者会将所有元素出栈并执行存活元素,显然不存在要考虑存活的问题。而只有or且为BiCompletion的情况下,才可能出现两个源之一实际并未完成,这样在非NESTED模式下调用cleanStack方法。

可见2的问题是存在的。但它对于整体的运行结果是无影响的,后续该source执行完毕,调用自身的postComplete时,将已死亡的Completion出栈并tryFire,会发现诸如”dep=null"等情况,直接返回null,则postComplete方法中的f会保持指向this并继续迭代下一个栈元素。

目前关于2中提到的cleanStack的调用只出现在UniCompletion成功后调用postFire时依赖模式和result运行。其实还有一种情况,就是前面提了一次的,属于future接口的get方法,以及类似的join方法。

前面提到,get和join方法都会在获取不到结果是按条件轮循watingGet方法,下面来看waitingGet方法。

private Object waitingGet(boolean interruptible) {
    Signaller q = null;//信号器
    boolean queued = false;//是否入队
    int spins = -1;//自旋次数
    Object r;//结果引用
    //循环条件是只等待result,内部有根据扰动决定的break
    while ((r = result) == null) {
        //自旋次数只有第一次进来是负值,后续只能是0或其他正数。
        if (spins < 0)
        //自旋次数,多处理器下初始化为16,否则为0,即不自旋。设置值后此次循环结束。
            spins = (Runtime.getRuntime().availableProcessors() > 1) ?
                1 << 8 : 0;
        //第二次循环时才会判断自旋次数。只要spins大于0就继续循环,直到达到0为止再执行下面的else代码。
        else if (spins > 0) {
            //仅当下一个种子数不小于0时,减小一次自旋次数。nextSecondarySeed是Thread类中使用@Contended注解标识的变量,
            //这与传说中的伪共享有关。
            if (ThreadLocalRandom.nextSecondarySeed() >= 0)
                --spins;
        }
        //停止自旋后的第一轮循环,result依旧是null,则对q进行初始化,关于Signaller后续再讲。
        else if (q == null)
            q = new Signaller(interruptible, 0L, 0L);
        //初始化q后的下一轮循环(停止自旋后的第二轮),queued是false,将上一轮循环初始化的q压入栈。
        else if (!queued)
            queued = tryPushStack(q);
        //停止自旋后的若干次循环(上一步可能压栈失败,则下一轮自旋会再次压栈,直到成功)后,判断是否可扰动。
        else if (interruptible && q.interruptControl < 0) {
            //扰动信号匹配,将q的有关字段全部置空,顺带清一下栈,返回null。
            q.thread = null;
            //这个清栈的过程,细看上面的解释还有有关的源码,可能会发出一个疑问,cleanStack只能清除isLive判断false的Completion,
            //但目前的实现,基本上都只能在dep为null,base为null等仅当dep执行完成的情况发生,而dep完成的情况是当前CompletableFuture的
            //result不是null,而方法运行到此,很明显result必然是null,那么还有必要清栈吗?
            //答案是必要的,首先将来也许能出现存活或死亡状态与source的result无关的Completion,那么此处清一下栈也是帮助后面的工作。
            //其次,刚才压入栈的q在thread指向null时即已死亡,它也必须要进行清除。
            cleanStack();
            return null;
        }
        else if (q.thread != null && result == null) {
            //q关联的线程存在,即q存活,且依旧没有执行完毕,使用ForkJoinPool的阻塞管理机制,q的策略进行阻塞。
            try {
                ForkJoinPool.managedBlock(q);
            } catch (InterruptedException ie) {
                //阻塞是可以扰动的,此时会将q的扰动控制信号设置为-1,则下一次循环时将可能进入上一个else if。
                q.interruptControl = -1;
            }
        }
    }
    //前面的循环没有break,能执行到此,只有result获得非null值的情况。
    if (q != null) {
        //若q不是null,说明没有在自旋阶段获取到result,需要对它进行禁用。
        q.thread = null;
        if (q.interruptControl < 0) {
            if (interruptible)
                //可扰动且有扰动信号,则说明扰动后未能进入上面带有cleanStack的那个else if,
                //可能是恰好在这次循环开始时获取到了非空result,从而退出循环,也可能是参数interruptible为假,
                //在外部扰动了当前线程后,依旧等到了result。
                //只要发生了扰动,就将结果置null,外面调用者如果是join,可以报出扰动。
                r = null; // report interruption
            else
                //如果不可扰动,则中断当前线程(创建q的线程)。
                Thread.currentThread().interrupt();
        }
    }
    //当前future已经有结果,进行postComplete逻辑并返回r。
    postComplete();
    return r;
}

根据该方法的注释,waitingGet方法只会有两个结果,null(可扰动并且扰动了)和原始的result。而get方法可扰动,也即可返回null,join方法不可扰动,只能等待结束或抛出异常。

waitingGet方法中出现了第三个也是最后一个Completion的直接子类Signaller,前面没有对它进行介绍,不过它也只使用在此处,因此可以一并介绍。

static final class Signaller extends Completion
    implements ForkJoinPool.ManagedBlocker {
    long nanos;                    // 计时的情况下,要等待的时间。
    final long deadline;           // 计时的情况下指定不为0的值
    volatile int interruptControl; // 大于0代表可扰动,小于0代表已扰动。
    volatile Thread thread;//持有的线程
 
    Signaller(boolean interruptible, long nanos, long deadline) {
        this.thread = Thread.currentThread();
        this.interruptControl = interruptible ? 1 : 0;//不可扰动,赋0
        this.nanos = nanos;
        this.deadline = deadline;
    }
    final CompletableFuture<?> tryFire(int ignore) {//ignore无用
        Thread w; //Signaller自持有创建者线程,tryFire只是单纯唤醒创建它的线程。
        if ((w = thread) != null) {
            thread = null;//释放引用
            LockSupport.unpark(w);//解除停顿。
        }
        //返回null,当action已执行并进行postComlete调用时,f依旧指向当前CompletableFuture引用并解除停顿。
        return null;
    }
    public boolean isReleasable() {
        //线程是空,允许释放。这可能是某一次调用本方法或tryFire方法造成。
        if (thread == null)
            return true;
        if (Thread.interrupted()) {
            //如果调用isReleasable方法的线程被扰动了,则置扰动信号为-1
            int i = interruptControl;
            interruptControl = -1;
            if (i > 0)
            //原扰动信号是”可扰动“,则是本次调用置为”已扰动“,返回true。
                return true;
        }
        //未定时(deadline是0)的情况只能在上面释放,定时的情况,本次计算nanos(deadline-System.nanoTime())
        //或上次计算的nanos不大于0时,说明可以释放。
        if (deadline != 0L &&
            (nanos <= 0L || (nanos = deadline - System.nanoTime()) <= 0L)) {
            //只要可释放,将创建者线程的引用释放。下次调用直接返回true,线程运行结束销毁后可被gc回收。
            thread = null;
            return true;
        }
        //仍持有创建者线程,调用此方法的线程未扰动或当前扰动不是第一次,未定时或不满足定时设置的一律返回false。
        return false;
    }
    public boolean block() {
        //block方法
        if (isReleasable())
            //判断可释放,直接return true。
            return true;
        //判断deadline是0,说明不计时,默认park。
        else if (deadline == 0L)
            LockSupport.park(this);
        else if (nanos > 0L)
            //计时情况,park指定nanos。
            LockSupport.parkNanos(this, nanos);
        //睡醒后再次返回isReleasable的结果。
        return isReleasable();
    }
    //创建者线程引用被释放即代表死亡。
    final boolean isLive() { return thread != null; }
}

Signaller是一个Completion的直接子类,同时实现了ForkJoinPool的内部接口ManagedBlocker,这使得它可以在当ForkJoinPool出现大量线程阻塞堆积时避免饥饿。
Signaller的作用是持有和释放一个线程,并提供相应的阻塞策略。
前面提到的waitingGet方法创建了一个Signaller(interruptible, 0L, 0L),类似的,可以看到timedGet方法使用Signaller(true, nanos, d == 0L ? 1L : d)来进行阻塞的管理,管理的方法依赖ForkJoinPool内部的

ForkJoinPool.managedBlock(q)来实现,而这用到了被Signaller实现的ForkJoinPool.ManagedBlocker,managedBlock方法源码如下。

//ForkJoinPool的managedBlock方法。
public static void managedBlock(ManagedBlocker blocker)
    throws InterruptedException {
    ForkJoinPool p;
    ForkJoinWorkerThread wt;
    Thread t = Thread.currentThread();//调用此方法的线程,即前面的Signaller的创建者线程。
    if ((t instanceof ForkJoinWorkerThread) &&
        (p = (wt = (ForkJoinWorkerThread)t).pool) != null) {
        //调用managedBlock方法的线程是ForkJoinWorkerThread,则它可运行在ForkJoinPool中。此处要求内部持有pool的引用。
        WorkQueue w = wt.workQueue;
        //循环,只要判断blocker(即Signaller)不可释放。
        while (!blocker.isReleasable()) {
            //尝试用ForkJoinPool对当前线程的工作队列进行补偿。
            //tryCompensate方法会尝试减少活跃数并可能创建或释放一个准备阻塞的worker线程,
            //它会在发生竞态,脏数据,松弛或池终止时返回false。
            //关于ForkJoinPool的详情单独准备文章。
            if (p.tryCompensate(w)) {
                 
                try {
                    //补偿成功,不停地对线程池尝试先isReleasable再block,任何一个方法返回true则终止循环。
                    do {} while (!blocker.isReleasable() &&
                                 !blocker.block());
                } finally {
                    //出现任何异常,或循环终止时,控制信号加上一个活跃数单元,因为前面通过补偿才会进入循环,已减少了一个单元。
                    U.getAndAddLong(p, CTL, AC_UNIT);
                }
                break;
            }
        }
    }
    else {
        //当前线程不是ForkJoinWorkerThread或不持有ForkJoinPool的引用。连续先尝试isReleasable再尝试block,直到有一者返回true为止。
        do {} while (!blocker.isReleasable() &&
                     !blocker.block());
    }
}

关于ForkJoinPool本文不做额外介绍,只列举这一个方法,到此为止,对于CompletableFuture的主要接口(继承自CompletionStage)和实现已经描述完毕(其实只过了一个特殊案例的接口,但是前面提到过,其他接口的逻辑和实现方式类似,无非就是run,active,apply的更换,或either,both,then,when等,有上面的基础,再凭借规则推测语义,源码并不难理解。

CompletableFuture还有一些独立声明的公有方法,源码也有些非常值得借鉴的地方,如allOf,anyOf两个方法。

//anyOf方法,返回一个CompletableFuture对象,任何一个cfs列表中的成员进入完成态(正常完成或异常),则它也一并完成,结果一致。
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
    //直接调用orTree
    return orTree(cfs, 0, cfs.length - 1);
}
//allOf方法,当所有cfs列表中的成员进入完成态后完成(使用空结果),或有任何一个列表成员异常完成时完成(使用同一个异常)。
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
    //直接调用andTree
    return andTree(cfs, 0, cfs.length - 1);
}
static CompletableFuture<Void> andTree(CompletableFuture<?>[] cfs,
                                       int lo, int hi) {
    //声明一个后续返回的dep
    CompletableFuture<Void> d = new CompletableFuture<Void>();
    if (lo > hi) //验参
        d.result = NIL;
    else {
        CompletableFuture<?> a, b;
        //折半验证参数并归并。每相邻的两个成员会在一个递归中生成另一个'd',
        //总量奇数的最后一个单独表示这个d。
        int mid = (lo + hi) >>> 1;
        if ((a = (lo == mid ? cfs[lo] :
                  andTree(cfs, lo, mid))) == null ||
            (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] :
                  andTree(cfs, mid+1, hi)))  == null)
            throw new NullPointerException();
        //调用d.biRelay的中继方法尝试完成。
        if (!d.biRelay(a, b)) {
            //不满足完成条件,生成一个中继并压栈,再次尝试同步完成。若不满足条件,ab任何一个完成后都会再间接调用它的tryFire。
            BiRelay<?,?> c = new BiRelay<>(d, a, b);
            a.bipush(b, c);//除非ab均完成,否则bipush要进ab两者的栈。
            c.tryFire(SYNC);
        }
    }
    return d;
}
//biRelay方法,有前面的基础,很简单,只要ab之一任何一个未完成则返回false,都完成且dep未完成则进入相应的正常异常完成策略,
//不论dep是否已完成,只要ab均已完成,则返回true
boolean biRelay(CompletableFuture<?> a, CompletableFuture<?> b) {
    Object r, s; Throwable x;
    if (a == null || (r = a.result) == null ||
        b == null || (s = b.result) == null)
        return false;
    //biRelay是尝试根据两个CompletableFuture完成dep,因为三个complete*方法均已做到原子性,也没有action要执行,因此它不需要claim。
    if (result == null) {
        if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
            completeThrowable(x, r);
        else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null)
            completeThrowable(x, s);
        else
            //正常情况,用null完成。
            completeNull();
    }
    return true;
}
 
 
//压入栈的BiRelay
static final class BiRelay<T,U> extends BiCompletion<T,U,Void> { // for And
    BiRelay(CompletableFuture<Void> dep,
            CompletableFuture<T> src,
            CompletableFuture<U> snd) {
        super(null, dep, src, snd);
    }
    final CompletableFuture<Void> tryFire(int mode) {
        CompletableFuture<Void> d;
        CompletableFuture<T> a;
        CompletableFuture<U> b;
        if ((d = dep) == null || !d.biRelay(a = src, b = snd))
            //已经完成过,或者未完成,本次也不能完成,返回一个null
            return null;
        //BiRelay通过BiCompletion间接继承了UniCompletion,因此dep取null代表死亡。
        //这样也能规避错误的tryFire,如当它已被完成,持有的dep引用置null,当d进行postFire的postComplete时会保持f=this并持续出栈
        //dep未完成时清栈也能有效移除已完成的任务。
        src = null; snd = null; dep = null;
        return d.postFire(a, b, mode);
    }
}
//orTree类似上面的andTree,有一个完成或异常,就用它的结果或异常作为返回的CompletableFuture的结果或异常。
static CompletableFuture<Object> orTree(CompletableFuture<?>[] cfs,
                                        int lo, int hi) {
    CompletableFuture<Object> d = new CompletableFuture<Object>();
    if (lo <= hi) {
        CompletableFuture<?> a, b;
        int mid = (lo + hi) >>> 1;
        //同上
        if ((a = (lo == mid ? cfs[lo] :
                  orTree(cfs, lo, mid))) == null ||
            (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] :
                  orTree(cfs, mid+1, hi)))  == null)
            throw new NullPointerException();
        //同上,下面简述orRelay和OrRelay
        if (!d.orRelay(a, b)) {
            OrRelay<?,?> c = new OrRelay<>(d, a, b);
            //除非ab任何一个已完成,否则orpush要进栈,且只进一个栈。
            a.orpush(b, c);
            c.tryFire(SYNC);
        }
    }
    return d;
}
//很明显,orRelay就是两个CompletableFuture的或关系中继者。
final boolean orRelay(CompletableFuture<?> a, CompletableFuture<?> b) {
    Object r;
    if (a == null || b == null ||
        ((r = a.result) == null && (r = b.result) == null))
        return false;
    //只要ab有一个有结果,立即进行完成工作。
    if (result == null)
        //前面已介绍过completeRelay函数,r可以是异常。
        completeRelay(r);
    //只要ab有一个完成或异常,即返回true
    return true;
}
static final class OrRelay<T,U> extends BiCompletion<T,U,Object> { // for Or
    OrRelay(CompletableFuture<Object> dep, CompletableFuture<T> src,
            CompletableFuture<U> snd) {
        super(null, dep, src, snd);
    }
    final CompletableFuture<Object> tryFire(int mode) {
        CompletableFuture<Object> d;
        CompletableFuture<T> a;
        CompletableFuture<U> b;
        if ((d = dep) == null || !d.orRelay(a = src, b = snd))
            return null;
        //置空的理由同上。
        src = null; snd = null; dep = null;
        return d.postFire(a, b, mode);
    }
}

后语

到此为止,关于CompletableFuture在JDK8的源码解析完成。

每一次读道格大神的代码时都会很痛苦,但是当痛苦变成豁然开朗时,接下来的是舒爽。

毫无疑问,道格大神的实现一如既往的优雅和巧妙,甚至不能用技术来形容,而应该用”艺术“来形容。

前面解释过选择CompletableFuture,因为在现有的若干框架中发现了与它的父接口CompletionStage有关,而CompletableFuture是官方给出的实现。同时,它也是响应式编程里的典型,在jdk出品的HttpClient中也有与之结合的部分。我相信前面的简单例子加上源码分析,再加上对api的稍加理解,很快就可以上手响应式编程。

简单回顾:

1.CompletableFuture实现了CompletionStage接口的一系列由either,apply,run,then等关键字组成的方法,可以链式调用。

2.CompletableFuture可以不去链式调用,每个CompletableFuture均可以单独调用多个1中提到的方法,结果是立即尝试执行这些方法,若执行条件不满足,也会自身维护一个栈的结构,栈中的各方法用单向链表的形式连接,在清栈操作或完成后置处理时按这个顺序或局部逆序fire,但任何时候执行时彼此之间不影响。

3.调用1中的方法并返回新的CompletableFuture的CompletableFuture在源码的注释中称为source,即源,生成的即d或dep,作者花了很久去理解这两个概念。源的完成一般为dep的基本条件,dep可以依赖一源或多源的完成,这里的完成包含正常result或异常result。

4.CompletableFuture对null结果和异常结果进行了编码,详情见上面的分析。

5.CompletableFuture基本无锁,get或join结果除外,但该锁阻塞的是查询线程,而不是用来计算和完成任务的线程,这些线程在CompletableFuture的代码中无锁,当然,可以在相应的栈元素中使用锁。

6.CompletableFuture中的栈以Completion为实现,它是ForkJoinTask,可以在ForkJoinPool中运行,它有各种派生类。各派生类可以实现任务的”生死“判断,”fire操作“,以及对有执行器(线程池或同步的执行器)时的逻辑处理。

7.通过CompletableFuture指定的action最多能有三种调用时机,但鉴于这太实现具体,将来随时可能变成四种五种,没有太大的参考价值。首先在action被声明时,若没有指定线程池,会进行一次调用尝试(1);若提供了线程池或(1)的尝试失败(一般是source未完成),则将生成的dep,源,action封装成一个Completion实例并入栈,入栈成功后进行第二次尝试,在这次尝试中,未提供执行器的会同步尝试一次fire(2);提供了执行器(线程池是一种异步的执行器)的,需要先claim,并只能claim一次,在claim方法中异步地执行,且这种情况下claim一定会失败,但会将执行器引用释放,下次claim因为cas的原因也一定会失败,这样保证了仅一次执行(3)。

一些api可以有一个线程池的成员变量,它可以在一开始在入口用线程池异步执行(静态方法为主,如 public static CompletableFuture<Void> runAsync(Runnable runnable) 这种入口方法,它负责第一个CompletableFuture的生成。


山人
146 声望48 粉丝