Future & CompletableFuture深入解析

深入浅出Future

使用示例

Callable & FutureTask

Callable<String> callableTask = () -> {  
    TimeUnit.SECONDS.sleep(2);  
    return "Callable Task Result";  
};  
  
FutureTask<String> futureTask = new FutureTask<>(callableTask);  
Thread thread = new Thread(futureTask);  
thread.start();
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {  
    try {  
        TimeUnit.SECONDS.sleep(1);  
    } catch (InterruptedException e) {  
        throw new IllegalStateException(e);  
    }  
    return "CompletableFuture Result";  
});

深入使用

以下示例来自:CompletableFuture原理与实践-外卖商家端API的异步化 - 美团技术团队 (meituan.com)
CF的使用是基于构造依赖树的,一个CompletableFuture的使用会触发另外一系列依赖它的CF执行
 

服务依赖


ExecutorService executor = Executors.newFixedThreadPool(5);  

CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> "result1",executor);  

CompletableFuture<String> cf2 = CompletableFuture.completedFuture("result2");


CompletableFuture<String> cf3 = cf1.thenApply(result -> {  
    System.out.println("result1: " + result);  
    return "result3";  
});  
CompletableFuture<String> cf4 = cf1.thenCombine(cf2, (result1, result2) -> {  
    
    return "result4";  
});

CompletableFuture<Void> cf6 = CompletableFuture.allOf(cf3, cf4, cf5);  
CompletableFuture<String> result = cf6.thenApply(v -> {  
    
    result3 = cf3.join();  
    result4 = cf4.join();  
    result5 = cf5.join();  
    
    return "result";  
});

源码解读(基于JDK1.8)

Callable

Callable是用于定义和返回结果,并且可能抛出异常的任务,类似于Runnable

@FunctionalInterface  
public interface Callable<V> {  
        
     V call() throws Exception;  
}

FutureTask

先梳理一下继承关系,Future是一个接口,定义了一些属性,然后

public interface RunnableFuture<V> extends Runnable, Future<V> {
    
    void run();
}

所以FutureTask就是一个Future的实际实现,是基于Runnable实现的

public class FutureTask<V> implements RunnableFuture<V> {
	
	private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;

	
	
	private Callable<V> callable;
	
    private Object outcome; 
	
    private volatile Thread runner;
	
    private volatile WaitNode waiters;
	
	private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }
    
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       
    }
    
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
		
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
		
        return report(s);
    }
    
    
    public void run() {
	    
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
					
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
					
                    set(result);
            }
        } finally {
            
            
			
            runner = null;
            
            
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
    
    ...
    
    
}

通过分析源码,我们发现Future获取结果的方式是通过阻塞实现的,会阻塞当前线程,如何避免阻塞呢?

CompletableFuture(以下简称CF)

为何会选择CF呢?

  • 可组合:可以将多个依赖操作通过不同的方式进行编排
  • 操作融合:将数据流中使用的多个操作符以某中方式组合起来从而降低开销
  • 延迟执行
  • 学习成本低
  • 相比于只能通过阻塞或者轮询获得结果而且不支持回调方法的Future,CF支持回调的方式进行处理结果,同时支持组合操作支持进一步的编排
定义

让我们首先关注它的定义

public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
	...
}

可以发现奥秘就在CompletionStage这个接口中,CompletionStage用于标识执行过程中的一个步骤(Stage),从而实现了服务编排

CompletionStage

定义了一系列任务的步骤,具体实现看CompletableFuture是如何实现的

基本属性
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
	
	volatile Object result;      
	
    volatile Completion stack;    
}

从这里我们看出,使用了类似于”观察者”模式设计思想,被观察者是CompletableFuture,而观察者是这些需要回调的依赖动作

Completion的定义

Completion是定义在CompletableFuture里的

abstract static class Completion extends ForkJoinTask<Void>
       implements Runnable, AsynchronousCompletionTask {
       volatile Completion next;      

       
       abstract CompletableFuture<?> tryFire(int mode);

       
       abstract boolean isLive();

       public final void run()                { tryFire(ASYNC); }
       public final boolean exec()            { tryFire(ASYNC); return false; }
       public final Void getRawResult()       { return null; }
       public final void setRawResult(Void v) {}
   }
依赖的流程
public <U> CompletableFuture<U> thenApply(  
    Function<? super T,? extends U> fn) {  
    return uniApplyStage(null, fn);  
}
private <V> CompletableFuture<V> uniApplyStage(  
    Executor e, Function<? super T,? extends V> f) {  
    if (f == null) throw new NullPointerException();  
    Object r;  
    
    if ((r = result) != null)  
        return uniApplyNow(r, e, f);  
    
    CompletableFuture<V> d = newIncompleteFuture();  
	
    unipush(new UniApply<T,V>(e, d, this, f));  
    return d;  
}

private <V> CompletableFuture<V> uniApplyNow(  
    Object r, Executor e, Function<? super T,? extends V> f) {  
    Throwable x;  
	
    CompletableFuture<V> d = newIncompleteFuture();  
    if (r instanceof AltResult) {  
        if ((x = ((AltResult)r).ex) != null) {  
            d.result = encodeThrowable(x, r);  
            return d;  
        }        r = null;  
    }    try {  
        if (e != null) {  
			
            e.execute(new UniApply<T,V>(null, d, this, f));  
        } else {  
            @SuppressWarnings("unchecked") T t = (T) r;  
            d.result = d.encodeValue(f.apply(t));  
        }    } catch (Throwable ex) {  
        d.result = encodeThrowable(ex);  
    }    return d;  
}

 final void unipush(Completion c) {
        if (c != null) {
			
            while (!tryPushStack(c)) {
                if (result != null) {
                    NEXT.set(c, null);
                    break;
                }
            }
            if (result != null)
                c.tryFire(SYNC);
        }
    }
  • 简单理解一下就是,在CF完成任务时,会去观察者链中出栈一个,然后执行,并且返回一个新的CompletableFuture,然后后续继续去完成后续依赖任务
  • 如果原始任务还没完成,那么就会将新的任务推入栈中,等待原始任务完成
执行过程

以 supplyAsync为例


public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,  
                                                   Executor executor) {  
    return asyncSupplyStage(screenExecutor(executor), supplier);  
}


private static final Executor ASYNC_POOL = USE_COMMON_POOL ?  
    ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {  
    return asyncSupplyStage(ASYNC_POOL, supplier);  
}

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

通过阅读源码,我们可以发现,原来CompletableFuture执行任务是会使用线程池去执行的,相比FutureTask通过阻塞去等待结果,确实是提升了性能。

关于线程池的源码和原理解析,以及一些八股知识,欢迎翻看鄙人的另一篇《深入浅出线程池》博客观看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值