Java——CompletableFuture原理

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()主要逻辑:

  1. 等待执行主任务Supplier.get(),执行完后通过CAS将结果保存到result,注意这里result用了volatile修饰保证实时性,因为类中方法很多需要判断result是否为空来做操作;

  2. 主任务执行成功后,通知执行依赖任务,在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;
}

逻辑:

  1. uniAccept()判断主任务是否执行完,执行完的话直接accept()运行依赖任务;
  2. 主任务没执行完的话,uniAccept()返回false,进入if逻辑中,构造UniAccept加入到Completion链表中,对应主任务执行完后的postComplete()逻辑;
  3. 最后调用UniAccept.tryFire()尝试执行依赖任务,其中也是调用步骤1uniAccept().

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);
    }
}

UniAcceptCompletion其中的一个子类,Completion还有很多子类,分别对应不同的依赖任务操作。其中主要方法是tryFire()尝试执行依赖任务,实质也是调用了uniAccept().

3. 总结

CompletableFuture原理为:异步执行主任务,将依赖任务加入到链表中,主任务执行完从链表中获取依赖任务执行。这时会有个疑问,依赖任务还没加入到链表中,主任务就执行完了,怎么办?所以添加依赖任务的时候,会判断主任务是否执行完,如果执行完则立即执行依赖任务,不添加到链表中。



世界那么大,感谢遇见,未来可期…

欢迎同频共振的那一部分人

作者公众号:Tarzan写bug

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值