并发编程之CompletableFuture异步编排的详细介绍和基本使用(全网最细最全)

CompletableFuture 类实现了 Future 接口,所以你还是可以像以前一样通过`get`方法阻塞或
者轮询的方式获得结果,但是这种方式不推荐使用。
CompletableFuture 和 FutureTask 同属于 Future 接口的实现类,都可以获取线程的执行结果
Future 是 Java 5 添加的类,用来描述一个异步计算的结果。你可以使用`isDone`方法检查计算是否完成,或者使用`get`阻塞住调用线程,直到计算完成返回结果,你也可以使用`cancel` 方法停止任务的执行。
虽然`Future`以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不
方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的
初衷相违背,轮询的方式又会耗费无谓的 CPU 资源,而且也不能及时地得到计算结果,为
什么不能用观察者设计模式当计算结果完成及时通知监听者呢?
很多语言,比如 Node.js,采用回调的方式实现异步编程。Java 的一些框架,比如 Netty,自
己扩展了 Java 的 `Future`接口,提供了`addListener`等多个扩展方法;Google guava 也提供了
通用的扩展 Future;Scala 也提供了简单易用且功能强大的 Future/Promise 异步编程模式。
作为正统的 Java 类库,是不是应该做点什么,加强一下自身库的功能呢?
在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture,提供了非常强大的
Future 的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以
通过回调的方式处理计算结果,并且提供了转换和组合 CompletableFuture 的方法。

在介绍CompletableFuture之前我们先来复习下创建线程的几种方式:

  1. 继承Thread

    public class ThreadTest {
        public static void main(String[] args) {
            System.out.println("main-----start");
            Thread thread = new Thread01();
            //启动执行
            thread.start();
            System.out.println("main-----end");
        }
        public static class Thread01 extends Thread{
            @Override
            public void run() {
                System.out.println("当前线程"+Thread.currentThread().getId());
                int i = 10/2;
                System.out.println("结果"+i);
            }
        }
    }
    

    2.实现Runnable接口

public class ThreadTest {
    public static void main(String[] args) {
        System.out.println("main-----start");
        Runable01 runable01 = new Runable01();
        new Thread(runable01).start();
        System.out.println("main-----end");
    }

    public static class Runable01 implements Runnable {
        @Override
        public void run() {
            System.out.println("当前线程" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("结果" + i);
        }
    }
}

3.实现Callable接口+FutureTask(可以拿到返回结果,可以处理异常)

public class ThreadTest {
    public static void main(String[] args) {
        System.out.println("main-----start");
        FutureTask<Integer> integerFutureTask = new FutureTask<>(new Callable01());
        new Thread(integerFutureTask).start();
        System.out.println("main-----end");
    }
    public static class Callable01 implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("结果" + i);
            return i;
        }
    }
}

Callable是jdk1.5以后添加的方法,可以用泛型来确定其返回值类型。我们点进去源码可以看到:实现的的RunnavleFuture这个接口,当我们继续深入后没错继承的还是Runnable,同时也继承了Future。所以启动时候先把Callable封装成Futureteask然后开启执行。当我们业务有一个计算结果,就可以用FutureTask的get方法来获取到结果:
public class ThreadTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-----start");
        FutureTask<Integer> integerFutureTask = new FutureTask<>(new Callable01());
        new Thread(integerFutureTask).start();
            //获取其返回结果
        Integer integer = integerFutureTask.get();
        System.out.println("main-----end"+integer);
    }
    public static class Callable01 implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("结果" + i);
            return i;
        }
    }
}

其打印的内容发现和之前有所不同,因为获取到其返回结果这个要等待才会打印main-end,所以这个等待是一个阻塞等待。那么只有Callable才可以用Future接收吗?我们看一下源码的内容:很明显并不是,我们想让runnable传一个对象,用这个对象来接收返回值就可以。

4.使用线程池

先思考为什么我们需要使用线程池,假设我们每次执行一个任务都new一个线程,就好比公司每当有新的事情要做的时候就招一个员工,早晚有一天会把公司的资源耗尽。所以在业务代码中,以上三种启动线程的方式都不用,因为可能会导致资源耗尽,我们应该将所有的多线程异步任务交给线程池使用。

4.1创建一个线程池:

public class ThreadTest {
    //当前系统中线程池最好只有一两个,每一个异步任务直接提交给线程池,让他自己去执行
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-----start");
        executorService.submit(new Runable01());
        System.out.println("main-----end");
    }
    public static class Runable01 implements Runnable {
        @Override
        public void run() {
            System.out.println("当前线程" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("结果" + i);
        }
    }
}

当一个方法的运行需要依靠另一个线程的方法的结果时,需要编排好其关系等,所以我们可以使用CompletableFuture来解决这个问题

业务场景假设:查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间。

假如商品详情页的每个查询,需要如下标注的时间才能完成,那么,用户需要 5.5s 后才能看到商品详情页的内容。很显然是不能接受的。如果有多个线程同时完成这 6 步操作,也许只需要 1.5s 即可完成响应 。这时候我们需要异步,但是我们发现步骤1、2、3可以同时执行,但是步骤4需要步骤1完成后才可以执行,而5、6更依赖1的结果.其中的关系管理很复杂,CompletableFuture就显得很合适了。

开始使用CompletableFuture

1.创建异步对象

CompletableFuture提供了四个静态方法来创建一个异步操作

    public static CompletableFuture<Void> runAsync(Runnable runnable) {
        return asyncRunStage(asyncPool, runnable);
    }
     */
    public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                   Executor executor) {
        return asyncRunStage(screenExecutor(executor), runnable);
    }
    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                       Executor executor) {
        return asyncSupplyStage(screenExecutor(executor), supplier);
    }
        public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
        return asyncSupplyStage(asyncPool, supplier);
    }

1.runXxxx都是没有返回结果的,supplyxxx都是可以获取返回结果的

2.可以传入自定义的线程池,否则用默认的线程池

public static ExecutorService executorService = Executors.newFixedThreadPool(10);    
public static void main(String[] args) {
        System.out.println("main--statr");
        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
            System.out.println("当前线程" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("结果" + i);
        }, executorService);
        System.out.println("main--end");
    }

测试一下,发现的确是异步的没有阻塞 ,并且我们以后都是用自定义的线程池来实现异步

测试第二个有返回值的:

    public static ExecutorService executorService = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main--statr");
        CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("结果" + i);
            return i;
        }, executorService);
        Integer integer = integerCompletableFuture.get();
        System.out.println("main--end"+integer);
    }

从源码可以看到其接收参数使用的是future接口,所以我们使用其get方法就可以得到返回值

 

2.计算完成时回调方法:

我们可以在异步业务代码下自己写一些需要执行的代码,按照逻辑一行一行执行,也可以使用CompletableFuture提供的方法来实现:

    public CompletableFuture<T> whenComplete(
        BiConsumer<? super T, ? super Throwable> action) {
        return uniWhenCompleteStage(null, action);
    }

    public CompletableFuture<T> whenCompleteAsync(
        BiConsumer<? super T, ? super Throwable> action) {
        return uniWhenCompleteStage(asyncPool, action);
    }

    public CompletableFuture<T> whenCompleteAsync(
        BiConsumer<? super T, ? super Throwable> action, Executor executor) {
        return uniWhenCompleteStage(screenExecutor(executor), action);
    }
    public CompletableFuture<T> exceptionally(
        Function<Throwable, ? extends T> fn) {
        return uniExceptionallyStage(fn);
    }

whenComplete 可以处理正常和异常的计算结果,exceptionally 处理异常情况。
whenComplete 和 whenCompleteAsync 的区别:
whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。
whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。
方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)

    public static ExecutorService executorService = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main--statr");
        CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("结果" + i);
            return i;
        }, executorService).whenComplete((res,excption)->{
            System.out.println("异步任务完成.....结果是"+res+"异常是:"+excption);
        });
        System.out.println("main--end");
    }

我们继续链式调用,看到whenComplete有两个参数,前者是结果后者是异常,我们执行看结果。

当我们出现异常时会咋样,我们把i=10/2改为i=10/0:

 我们可以看到其结果是null,异常为运算异常。

如果我们想出现异常后继续像已处理我们可以使用exceptionally()方法来执行,其方法接收一个T,返回一个R

   //当前系统中线程池最好只有一两个,每一个异步任务直接提交给线程池,让他自己去执行
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main--statr");
        CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程" + Thread.currentThread().getId());
            int i = 10 / 0;
            System.out.println("结果" + i);
            return i;
        }, executorService).whenComplete((res,excption)->{
            //虽然能得到异常信息,但是没发修改返回数据,类似于监听器
            System.out.println("异步任务完成.....结果是"+res+"异常是:"+excption);
        }).exceptionally(throwable -> {
            //可以感知异常,同时返回默认值
         return 10;
        });
        Integer integer = integerCompletableFuture.get();
        System.out.println("main--end"+integer);
    }

当我们发生异常后,可以感知到发生,并修改其返回值,这与whenComplete方法不同,后者只能作为一个监听器来使用。

 3.handle方法: 

和 complete 一样,可对结果做最后的处理(可处理异常),可改变返回值。

    public <U> CompletableFuture<U> handle(
        BiFunction<? super T, Throwable, ? extends U> fn) {
        return uniHandleStage(null, fn);
    }

    public <U> CompletableFuture<U> handleAsync(
        BiFunction<? super T, Throwable, ? extends U> fn) {
        return uniHandleStage(asyncPool, fn);
    }

    public <U> CompletableFuture<U> handleAsync(
        BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) {
        return uniHandleStage(screenExecutor(executor), fn);
    }

可以看到handle传入一个BiFunction方法,传入两个参数返回一个参数,所以其能感知到返回结果,也能感知到其异常修改结果。

 我们使用if判断其结果,如果没有异常,返回值乘2,如果异常发返回0,否则是1,然后根据最后返回值看一下其功能:

    public static ExecutorService executorService = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main--statr");
        //方法执行后的处理
        CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程" + Thread.currentThread().getId());
            int i = 10 / 0;
            System.out.println("结果" + i);
            return i;
        }, executorService).handle((res,thr)->{
            if(res!=null){
                return res*2;
            }else if(thr!=null){
                return 0;
            }
            return 1;
        });
        Integer integer = integerCompletableFuture.get();
        System.out.println("main--end" + integer);
    }

我们看到返回的是0,说明 感知到了异常并做出了判断。

4.线程串行化:

某些时候我们需要把两个异步任务串联起来,就可以用到这些方法

    public <U> CompletableFuture<U> thenApply(
        Function<? super T,? extends U> fn) {
        return uniApplyStage(null, fn);
    }

    public <U> CompletableFuture<U> thenApplyAsync(
        Function<? super T,? extends U> fn) {
        return uniApplyStage(asyncPool, fn);
    }

    public <U> CompletableFuture<U> thenApplyAsync(
        Function<? super T,? extends U> fn, Executor executor) {
        return uniApplyStage(screenExecutor(executor), fn);
    }

    public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
        return uniAcceptStage(null, action);
    }

    public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {
        return uniAcceptStage(asyncPool, action);
    }

    public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,
                                                   Executor executor) {
        return uniAcceptStage(screenExecutor(executor), action);
    }

    public CompletableFuture<Void> thenRun(Runnable action) {
        return uniRunStage(null, action);
    }

    public CompletableFuture<Void> thenRunAsync(Runnable action) {
        return uniRunStage(asyncPool, action);
    }

    public CompletableFuture<Void> thenRunAsync(Runnable action,
                                                Executor executor) {
        return uniRunStage(screenExecutor(executor), action);
    }

我们倒着看,假如A是ComletableFutre启动的异步任务则它就可调用thenRun**的异步任务,如果加了Async是代表新加一个线程执行任务,如果不加的话代表与A共同一个线程无法感知上一步的执行结果)。而thenAccept**方法是代表着我们如果还想接收A的异步结果并继续使用,则就可以用该方法(可以感知上一步的执行结果)。而当我们用了A的返回值处理后还要把自己的返回值进行返回进行处理则使用thenApply**方法,所以有两个泛型,一个是自己的返回值结果一个是上一个方法的值。

测试:

thenRun***

    public static void main(String[] args) {
        System.out.println("main--statr");
        //方法执行后的处理
        CompletableFuture<Void> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程" + Thread.currentThread().getId());
            int i = 10 / 5;
            System.out.println("结果" + i);
            return i;
        }, executorService).thenRunAsync(() -> {
            System.out.println("任务2启动了");
        }, executorService);
        System.out.println("main--end");
    }

发现返回值是void,这样我们无法感知其执行后的结果 所以我们使用thenAcceptAsync

    public static void main(String[] args) {
        System.out.println("main--statr");
        //方法执行后的处理
        CompletableFuture<Void> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程" + Thread.currentThread().getId());
            int i = 10 / 5;
            System.out.println("结果" + i);
            return i;
        }, executorService).thenAcceptAsync(result->{
            System.out.println(++result);
        },executorService);
        System.out.println("main--end");
    }

 上一个的返回值i=2经过++后输出发现的确是3,说明我们得到了其返回值。但是如果说我们这次的异步任务也要把最后的结果返回值供别人使用,就要使用thenApplyAsync方法

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main--statr");
        //方法执行后的处理
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程" + Thread.currentThread().getId());
            int i = 10 / 5;
            System.out.println("结果" + i);
            return i;
        }, executorService).thenApplyAsync(result -> {
            System.out.println(++result);
            return ++result;
        }, executorService);
        System.out.println(System.currentTimeMillis());
        System.out.println("执行完了————————模拟上面的方法运行久一些");
        Thread.sleep(1000);
        System.out.println("睡醒");
        System.out.println(System.currentTimeMillis());
        Integer integer = future.get();
        System.out.println("main--end"+integer);
    }

我故意加上了一个睡眠,可以看出假如最后那个异步方法要执行很久才有值,我们最后去等待get其值并打印是一个阻塞状态。 

两任务组合-都完成:

表示两个任务都ok,才完成某个任务

     public <U,V> CompletableFuture<V> thenCombine(
        CompletionStage<? extends U> other,
        BiFunction<? super T,? super U,? extends V> fn) {
        return biApplyStage(null, other, fn);
    }

    public <U,V> CompletableFuture<V> thenCombineAsync(
        CompletionStage<? extends U> other,
        BiFunction<? super T,? super U,? extends V> fn) {
        return biApplyStage(asyncPool, other, fn);
    }

    public <U,V> CompletableFuture<V> thenCombineAsync(
        CompletionStage<? extends U> other,
        BiFunction<? super T,? super U,? extends V> fn, Executor executor) {
        return biApplyStage(screenExecutor(executor), other, fn);
    }    
    public <U> CompletableFuture<Void> thenAcceptBoth(
        CompletionStage<? extends U> other,
        BiConsumer<? super T, ? super U> action) {
        return biAcceptStage(null, other, action);
    }

    public <U> CompletableFuture<Void> thenAcceptBothAsync(
        CompletionStage<? extends U> other,
        BiConsumer<? super T, ? super U> action) {
        return biAcceptStage(asyncPool, other, action);
    }

    public <U> CompletableFuture<Void> thenAcceptBothAsync(
        CompletionStage<? extends U> other,
        BiConsumer<? super T, ? super U> action, Executor executor) {
        return biAcceptStage(screenExecutor(executor), other, action);
    }

    public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,
                                                Runnable action) {
        return biRunStage(null, other, action);
    }

    public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
                                                     Runnable action) {
        return biRunStage(asyncPool, other, action);
    }

    public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
                                                     Runnable action,
                                                     Executor executor) {
        return biRunStage(screenExecutor(executor), other, action);
    }

thenCombine:组合两个 future,获取两个 future 的返回结果,并返回当前任务的返回值

BiFunction有三个泛型,分别是A,B方法的返回值和自己方法的返回值。
thenAcceptBoth:组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有
返回值。这里有一个Biconsumer,它代表着可以传A,B两个方法的值,拿到值后处理任务。
runAfterBoth:组合两个 future,不需要获取 future 的结果,只需两个 future 处理完任务后,
处理该任务。这里有一个CompletionStage<>,从源码上看

其实我们使用CompletableFuture也是实现了该接口,也就是说我们要再传递一个异步任务而已。 

任务组合代码展示:

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /**
         * 两个都完成
         */
        System.out.println("main--statr");
        //方法执行后的处理
        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程结束");
            return "hell";
        }, executorService);
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程结束");
            return "hello";
        }, executorService);
        future01.runAfterBothAsync(future02,()->{
            System.out.println("任务3开始");
        },executorService);
        System.out.println("main--end");
    }

 只有任务1,2执行完才任务3才可以ok,但我们获取不到前两个的返回值,所以我们使用thenAcceptBoth***方法:

  //当前系统中线程池最好只有一两个,每一个异步任务直接提交给线程池,让他自己去执行
    private static ThreadPoolExecutor executorService = new ThreadPoolExecutor(
            10,
            20,
            5,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(10000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /**
         * 两个都完成
         */
        System.out.println("main--statr");
        //方法执行后的处理
        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程结束");
            return "hell";
        }, executorService);
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程结束");
            return "hello";
        }, executorService);
        future01.thenAcceptBothAsync(future02,(f1,f2)->{
            System.out.println("任务3开始>>>"+f1+"___________"+f2);
        },executorService);
        System.out.println("main--end");
    }

已经获取到了,但是无法返回自己的这次执行方法所做的事情的结果,所以我们需要使用另一个叫做thenCombine***的方法。

    //当前系统中线程池最好只有一两个,每一个异步任务直接提交给线程池,让他自己去执行
    private static ThreadPoolExecutor executorService = new ThreadPoolExecutor(
            10,
            20,
            5,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(10000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /**
         * 两个都完成
         */
        System.out.println("main--statr");
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程结束");
            return "hello";
        }, executorService);
        //方法执行后的处理
        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程结束");
            return "hell";
        }, executorService);

        CompletableFuture<String> stringCompletableFuture = future01.thenCombineAsync(future02, (f1, f2) -> {
            System.out.println("任务3开始>>>" + f1 + "___________" + f2);
            return "111";
        }, executorService);
        System.out.println(stringCompletableFuture.get());
        System.out.println("main--end");
    }

 

这样就可以做到把任务三的返回值取到并调用了。

两任务组合-完成任意一个:

刚刚我们使用的是两个都完成才可以执行,而这是只需要一个就ok

    public <U> CompletableFuture<U> applyToEither(
        CompletionStage<? extends T> other, Function<? super T, U> fn) {
        return orApplyStage(null, other, fn);
    }

    public <U> CompletableFuture<U> applyToEitherAsync(
        CompletionStage<? extends T> other, Function<? super T, U> fn) {
        return orApplyStage(asyncPool, other, fn);
    }

    public <U> CompletableFuture<U> applyToEitherAsync(
        CompletionStage<? extends T> other, Function<? super T, U> fn,
        Executor executor) {
        return orApplyStage(screenExecutor(executor), other, fn);
    }

    public CompletableFuture<Void> acceptEither(
        CompletionStage<? extends T> other, Consumer<? super T> action) {
        return orAcceptStage(null, other, action);
    }

    public CompletableFuture<Void> acceptEitherAsync(
        CompletionStage<? extends T> other, Consumer<? super T> action) {
        return orAcceptStage(asyncPool, other, action);
    }

    public CompletableFuture<Void> acceptEitherAsync(
        CompletionStage<? extends T> other, Consumer<? super T> action,
        Executor executor) {
        return orAcceptStage(screenExecutor(executor), other, action);
    }

    public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,
                                                  Runnable action) {
        return orRunStage(null, other, action);
    }

    public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
                                                       Runnable action) {
        return orRunStage(asyncPool, other, action);
    }

    public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
                                                       Runnable action,
                                                       Executor executor) {
        return orRunStage(screenExecutor(executor), other, action);
    }

applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。
acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。
runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返
回值。

    //当前系统中线程池最好只有一两个,每一个异步任务直接提交给线程池,让他自己去执行
    private static ThreadPoolExecutor executorService = new ThreadPoolExecutor(
            10,
            20,
            5,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(10000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /**
         * 只完成一个
         */
        System.out.println("main--statr");
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程结束");
            return "hello";
        }, executorService);
        //方法执行后的处理
        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程结束");
            return "hell";
        }, executorService);
        future01.runAfterEitherAsync(future02, ()-> {
            System.out.println("任务3开始>>>");
        }, executorService);
        System.out.println("main--end");
    }

我们使用future01和future02进行组合,然后只要有一个完成就可以执行

 那么如果有一个线程异常了呢?

    //当前系统中线程池最好只有一两个,每一个异步任务直接提交给线程池,让他自己去执行
    private static ThreadPoolExecutor executorService = new ThreadPoolExecutor(
            10,
            20,
            5,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(10000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    public static void main(String[] args){
        /**
         * 只完成一个
         */
        System.out.println("main--statr");
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            int i=10/0;
            System.out.println("任务2线程结束"+i);
            return "hello";
        }, executorService);
        //方法执行后的处理
        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程结束");
            return "hell";
        }, executorService);
        future01.runAfterEitherAsync(future02, ()-> {
            System.out.println("任务3开始>>>");
        }, executorService);
        System.out.println("main--end");
    }

我们让除数等于0,则运算时会发生异常,这时执行打印发现,任务3的确执行了

 而当两个都发生异常后,也不会有表示

    //当前系统中线程池最好只有一两个,每一个异步任务直接提交给线程池,让他自己去执行
    private static ThreadPoolExecutor executorService = new ThreadPoolExecutor(
            10,
            20,
            5,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(10000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    public static void main(String[] args){
        /**
         * 只完成一个
         */
        System.out.println("main--statr");
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            int i=10/0;
            System.out.println("任务2线程结束"+i);
            return "hello";
        }, executorService);
        //方法执行后的处理
        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程结束");
            int i=10/0;
            System.out.println("任务2线程结束"+i);
            return "hell";
        }, executorService);
        future01.runAfterEitherAsync(future02, ()-> {
            System.out.println("任务3开始>>>");
        }, executorService);
        System.out.println("main--end");
    }

 

如果我们要感知到线程1的结果则需要使用accepEitherAsunc方法

    //当前系统中线程池最好只有一两个,每一个异步任务直接提交给线程池,让他自己去执行
    private static ThreadPoolExecutor executorService = new ThreadPoolExecutor(
            10,
            20,
            5,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(10000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    public static void main(String[] args){
        /**
         * 只完成一个
         */
        System.out.println("main--statr");
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程结束");
            return "hello";
        }, executorService);
        //方法执行后的处理
        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程结束");
            System.out.println("任务2线程结束");
            return "hell";
        }, executorService);
        future01.acceptEitherAsync(future02,(res)-> {
            System.out.println("任务3开始>>>");
            System.out.println("线程1的结果"+res);
        }, executorService);
        System.out.println("main--end");
    }

 

 如果要返回自己的结果就需要使用 applyToEitherAsync方法

    //当前系统中线程池最好只有一两个,每一个异步任务直接提交给线程池,让他自己去执行
    private static ThreadPoolExecutor executorService = new ThreadPoolExecutor(
            10,
            20,
            5,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(10000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /**
         * 只完成一个
         */
        System.out.println("main--statr");
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程结束");
            return "hello";
        }, executorService);
        //方法执行后的处理
        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程结束");
            System.out.println("任务2线程结束");
            return "hell";
        }, executorService);
        CompletableFuture<String> stringCompletableFuture = future01.applyToEitherAsync(future02, (res) -> {
            System.out.println("任务3开始>>>");
            System.out.println("线程1的结果" + res);
            return "任务3也完事了";
        }, executorService);
        System.out.println(stringCompletableFuture.get());
        System.out.println("main--end");
    }

 因为get会阻塞相当于join,所以哪怕前几个方法都是异步的main--end还是会最后打印

多任务组合:

allOf:等待所有任务完成
anyOf:只要有一个任务完成

   public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
        return andTree(cfs, 0, cfs.length - 1);
    }

    /**
     * Returns a new CompletableFuture that is completed when any of
     * the given CompletableFutures complete, with the same result.
     * Otherwise, if it completed exceptionally, the returned
     * CompletableFuture also does so, with a CompletionException
     * holding this exception as its cause.  If no CompletableFutures
     * are provided, returns an incomplete CompletableFuture.
     *
     * @param cfs the CompletableFutures
     * @return a new CompletableFuture that is completed with the
     * result or exception of any of the given CompletableFutures when
     * one completes
     * @throws NullPointerException if the array or any of its elements are
     * {@code null}
     */
    public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
        return orTree(cfs, 0, cfs.length - 1);
    }

全都ok的展示:

    private static ThreadPoolExecutor executorService = new ThreadPoolExecutor(
            10,
            20,
            5,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(10000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /**
         * 只完成一个
         */
        System.out.println("main--statr");
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程结束");
            return "hello";
        }, executorService);
        //方法执行后的处理
        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程结束");
            return "hell";
        }, executorService);
        CompletableFuture<Void> allOf = CompletableFuture.allOf(future01, future02);
        System.out.println("--------------");
        allOf.get();
        System.out.println("main--end");
    }

任意一个ok的展示:

public class ThreadTest {
    //当前系统中线程池最好只有一两个,每一个异步任务直接提交给线程池,让他自己去执行
    private static ThreadPoolExecutor executorService = new ThreadPoolExecutor(
            10,
            20,
            5,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(10000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /**
         * 只完成一个
         */
        System.out.println("main--statr");
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程结束");
            return "hello";
        }, executorService);
        //方法执行后的处理
        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程结束");
            return "hell";
        }, executorService);
        CompletableFuture<Object> objectCompletableFuture = CompletableFuture.anyOf(future01, future02);
        System.out.println("--------------");;
        System.out.println("展示:"+objectCompletableFuture.get());
        System.out.println("main--end");
    }

 我们注意到这个是有返回值的其实,其返回的是第一个完成的那个线程的值;

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值