异步技巧之CompletableFuture

1.Future接口

1.1 什么是Future?

在jdk的官方的注解中写道

A {
   @code Future} represents the result of an asynchronous
 * computation.  Methods are provided to check if the computation is
 * complete, to wait for its completion, and to retrieve the result of
 * the computation.

在上面的注释中我们能知道Future用来代表异步的结果,并且提供了检查计算完成,等待完成,检索结果完成等方法。简而言之就是提供一个异步运算结果的一个建模。它可以让我们把耗时的操作从我们本身的调用线程中释放出来,只需要完成后再进行回调。就好像我们去饭店里面吃饭,不需要你去煮饭,而你这个时候可以做任何事,然后饭煮好后就会回调你去吃。

1.2 JDK8以前的Future

在JDK8以前的Future使用比较简单,我们只需要把我们需要用来异步计算的过程封装在Callable或者Runnable中,比如一些很耗时的操作(不能占用我们的调用线程时间的),然后再将它提交给我们的线程池ExecutorService。代码例子如下:

public static void main(String[] args) {
   
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new Callable<String>() {
   
            @Override
            public String call() throws Exception {
   
                return Thread.currentThread().getName();
            }
        });

        doSomethingElse();//在我们异步操作的同时一样可以做其他操作
        try {
   
            String res = future.get();
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        } catch (ExecutionException e) {
   
            e.printStackTrace();
        }
    }

上面展示了我们的线程可以并发方式调用另一个线程去做我们耗时的操作。当我们必须依赖我们的异步结果的时候我们就可以调用get方法去获得。当我们调用get方法的时候如果我们的任务完成就可以立马返回,但是如果任务没有完成就会阻塞,直到超时为止。

Future底层是怎么实现的呢? 我们首先来到我们ExecutorService的代码中submit方法这里会返回一个Future

public <T> Future<T> submit(Callable<T> task) {
   
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

在sumbmit中会对我们的Callable进行包装封装成我们的FutureTask,我们最后的Future其实也是Future的实现类FutureTask,FutureTask实现了Runnable接口所以这里直接调用execute。在FutureTask代码中的run方法代码如下:

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

可以看见当我们执行完成之后会set(result)来通知我们的结果完成了。set(result)代码如下:

protected void set(V v) {
   
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
   
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }

首先用CAS置换状态为完成,以及替换结果,当替换结果完成之后,才会替换为我们的最终状态,这里主要是怕我们设置完COMPLETING状态之后最终值还没有真正的赋值出去,而我们的get就去使用了,所以还会有个最终状态。我们的get()方法的代码如下:

public V get() throws InterruptedException, ExecutionException {
   
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

首先获得当前状态,然后判断状态是否完成,如果没有完成则进入awaitDone循环等待,这也是我们阻塞的代码,然后返回我们的最终结果。

1.2.1缺陷
我们的Future使用很简单,这也导致了如果我们想完成一些复杂的任务可能就比较难。比如下面一些例子:

将两个异步计算合成一个异步计算,这两个异步计算互相独立,同时第二个又依赖第一个的结果。
当Future集合中某个任务最快结束时,返回结果。
等待Future结合中的所有任务都完成。
通过编程方式完成一个Future任务的执行。

应对Future的完成时间。也就是我们的回调通知。

1.3CompletableFuture

CompletableFuture是JDK8提出的一个支持非阻塞的多功能的Future,同样也是实现了Future接口。

1.3.1CompletableFuture基本实现
下面会写一个比较简单的例子:

public static void main(String[] args) {
   
        CompletableFuture<String> completableFuture = new
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值