【JAVA】CompletableFuture笔记及关键源码解读-CompletionStage异步计算实现

   

目录

CompletionStage

CompletableFuture

CompletableFuture传递链实现

Completion

关键源码(以thenApply为例)

complete/completeExceptionally

obtrudeValue/obtrudeExceptionlly

 anyOf以及allof

小栗子


   最近因为项目需要开发了合作代理网关,它的主要功能就是加解签、入参转换、路由转发(支持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说明该动作异步执行(线程池可传,也可用默认线程池)。

方法说明触发条件
<U> CompletionStage<U>thenAppy(Function<? super T,? extends U> fn)
/thenApplyAsyn

当前Stage正常结束后触发Function执行,当前Stage的结果是Function的参数,并返回一个新的CompletionStage,新CompletionStage的结果是Function的结果或者异常

当前Stage完成,只有正常结束才执行动作
CompletionStage<Void> thenAccept(Consumer<? super T> action)
/thenAcceptAsync
当前Stage正常结束后触发Consumer执行,当前Stage的结果是Consumer的参数,并返回一个新的CompletionStage,新CompletionStage的结果是java.util.concurrent.CompletableFuture#NIL(null)或异常当前Stage完成,只有正常结束才执行动作
CompletionStage<Void> thenRun(Runnable action)
/thenRunAsync
当前Stage正常结束后触发Runnable执行,Runnable是没有参数的(跟thenAccept的区别),并返回一个新的CompletionStage,新CompletionStage的结果是NIL(null)或异常当前Stage完成,只有正常结束才执行动作
<U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
/thenComobineAsync
当前Stage以及"other"Stage都正常结束后触发BiFunction执行,当前Stage和"other"Stage的结果作为BiFunction的参数,并返回一个新的CompletionStage,新CompletionStage的结果是BiFunction的结果或者异常当前Stage和"other"Stage完成,只有正常结束才执行动作
<U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)
/thenAcceptBothAsync
当前Stage以及"other"Stage都正常结束后触发BiConsumer执行,当前Stage和"other"Stage的结果作为BiConsumer的参数,并返回一个新的CompletionStage,新CompletionStage是NIL(null)或异常当前Stage和"other"Stage完成,只有正常结束才执行动作
CompletionStage<Void> runAfterBoth(CompletionStage<?> other, Runnable action)
/runAfterBothAsync
当前Stage以及"other"Stage都正常结束后触发Runnalbe执行,Runnable没有参数,并返回一个新的CompletionStage,新CompletionStage的data对象类型Void当前Stage和"other"Stage完成,只有正常结束才执行动作
<U> CompletionStage<U> applyToEither (CompletionStage<? extends T> other, Function<? super T, U> fn)
/applyToEitherAsync
当前Stage或"other"Stage正常结束后触发Function执行,当前Stage或"other"Stage的结果作为Function的参数(所以"other"Stage的类型必须是当前Stage的子类),并返回一个新的CompletionStage,新CompletionStage的data对象类型是可以由Fucntion改变当前Stage或"other"Stage完成,只有正常结束才执行动作
CompletionStage<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
/acceptEitherAsync
当前Stage或"other"Stage正常结束后触发Consumer执行,当前Stage或"other"Stage的结果作为Consumer的参数(所以"other"Stage的类型必须是当前Stage的子类),并返回一个新的CompletionStage,新CompletionStage的结果是NIL(null)或异常当前Stage或"other"Stage完成,只有正常结束才执行动作
CompletionStage<Void> runAfterEither(CompletionStage<?> other, Runnable action)
/runAfterEitherAsyn
当前Stage或"other"Stage正常结束后触发Runnable执行,当前Stage或"other"Stage的结果作为Runnable的参数(所以"other"Stage的类型无关),并返回一个新的CompletionStage,新CompletionStage的结果是NIL(null)或异常当前Stage或"other"Stage完成,只有正常结束才执行动作
<U> CompletionStage<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
/thenComposeAsyn
当前Stage正常结束后触发Function执行,当前Stage结果作为Function的参数,并返回一个新的CompletionStage(称为最终Stage)。跟之前不一样的是Function返回的也是一个CompletionStage(中间Stage),只有中间Stage完成才会使最终Stage完成,最终Stage的结果是中间Stage的结果或异常

2阶段触发:

第一阶段:当前Stage完成,只有正常结束触发Function执行并返回中间Stage

第二阶段:中间Stage完成触发最终Stage完成

CompletionStage<T> exceptionally(Function<Throwable, ? extends T> fn)
当前Stage异常结束后触发Function执行,当前Stage的异常是Function的参数,并返回一个新的CompletionStage,新CompletionStage的结果是Function的结果或者当前Stage结果(正常结束)或者异常(Functioin抛出来)当前Stage完成,只有异常结束才执行动作
public CompletionStage<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
/whenCompleteAsync
当前Stage结束(正常/异常)后触发BiConsumer执行,当前Stage的result/excption是BiConsumer的参数,并返回一个新的CompletionStage,新CompletionStage的result和exception就是当前Stage的result和exception当前Stage完成,正常/异常结束都会执行动作
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
/handleAsync
当前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实现机制都是一个倒着的二叉树。

 

小栗子

没时间写了,后面再补充吧。快写吐血了。。。 

 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java计算机二级笔记主要包括Java语言基础、面向对象编程、Java集合框架和异常处理等内容。 Java语言基础部分主要包括Java的特点、发展历程、安装和配置开发环境等相关知识。学习者需要了解Java的特点,如面向对象、简单易学、平台无关性等。同时,了解Java的发展历程可以帮助学习者更好地理解Java的优势和应用场景。安装和配置开发环境是学习Java的第一步,学习者需要掌握如何安装JDK(Java Development Kit)和配置开发工具(如Eclipse、IntelliJ IDEA等)。 面向对象编程是Java的核心,学习者需要了解面向对象的基本概念和原则,如封装、继承、多态等。此外,还需要学习类和对象的创建和使用,掌握Java中的访问修饰符、构造方法、成员变量和成员方法等。 Java集合框架是Java中常用的数据结构和算法的封装,学习者需要掌握常用的集合类(如List、Set、Map等)的特点和使用方法,了解集合类的底层实现原理,掌握集合类的常用操作和遍历方式。 异常处理是Java中处理程序错误和异常情况的机制,学习者需要了解Java中的异常类型和异常处理的方式,掌握try-catch语句的基本用法,理解异常处理的原则和技巧。 在学习Java计算机二级的过程中,学习者可以通过阅读教材、参加培训班或自学网上资源等方式进行学习。此外,还可以通过编写小型的Java程序进行实践练习,加深对Java语言和相关知识的理解和掌握。总之,Java计算机二级的学习需要持续的练习和实践,不断提升编程能力和解决问题的能力。 ### 回答2: Java计算机二级是一种计算机等级考试,主要考察考生在Java编程方面的基本知识和技能。以下是关于Java计算机二级的笔记内容: 1. Java基础知识: - Java语言的起源和发展历史; - Java开发环境的搭建与配置; - Java的命名规范和常用关键字; - 数据类型和变量的定义与使用; - 运算符的种类和使用方法; - 控制流程语句,包括条件语句和循环语句。 2. 面向对象编程: - 类和对象的概念; - 封装、继承和多态的原理和实现; - 构造方法、静态方法和实例方法的区别和应用; - 抽象类和接口的定义和使用。 3. 异常处理: - 异常的分类和处理机制; - try-catch语句与异常捕获; - 异常的抛出和捕获; - 自定义异常及其使用。 4. 数组和集合: - 数组的定义和使用; - 多维数组的应用; - 集合的种类和特点; - List、Set和Map等集合框架的使用。 5. 文件操作: - 文件的读写操作; - 文件流的使用; - 字符流和字节流的区别和应用。 6. 网络编程: - 基本的网络通信概念; - Socket编程的原理和实现; - 常用HTTP请求和响应的处理。 以上是关于Java计算机二级的一些重要笔记内容,通过对这些知识点的学习和掌握,考生可以更好地准备和应对Java计算机二级考试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值