异步任务编排利器CompletableFuture实战

程序员黑哥 2023-12-19 22:10 发表于湖南

前言

CompletableFuture是Java8中并发包提供的异步编程实现类,它是Future接口的扩展接口,它拥有比Future更强大的能力。

Future接口回顾

Future接口提供下面方法

public interface Future<V> {    // 取消任务执行    boolean cancel(boolean mayInterruptIfRunning);    // 判断任务是否取消    boolean isCancelled();    // 任务是否完成    boolean isDone();    // 获取执行结果(阻塞)    V get() throws InterruptedException, ExecutionException;    // 最多等待给定的时间获取任务执行结果,否则抛出超时异常    V get(long timeout, TimeUnit unit)        throws InterruptedException, ExecutionException, TimeoutException;}
Future代码演示
// 1. 创建线程池ExecutorService executorService = Executors.newSingleThreadExecutor();// 2.创建FutureTask任务FutureTask<String> futureTask = new FutureTask<>(() -> {    TimeUnit.SECONDS.sleep(5);    return "任务执行成功";});// 3. 将FutureTask提交到线程池executorService.submit(futureTask);System.out.println(LocalTime.now() + "--->开始等待执行结果");// 4. 获取执行结果String result = futureTask.get();System.out.println(LocalTime.now() + "<---" + result);executorService.shutdown();

执行结果如下所示,主线程调用FutureTask.get()方法阻塞,等待了5秒才获取到执行结果

图片

Future接口的局限性
  • Future获取执行结果是阻塞的

Future并不支持回调,要想获取任务执行的结果,只能通过阻塞的get()方法或者轮询判断执行是否成功再获取执行结果

  • 多个Future无法合并

例如我们有一种场景,需要先执行10的Future,等10个Future任务全部执行完毕后,再运行某个函数,Future并不支持

  • Future接口没有异常处理API

Future没有异常处理API,如果抛出异常

CompletableFuture

CompletableFuture是对Future的增强,它不仅支持原来Future的能力,并在此基础上进行了扩展,同时CompletableFuture实现了对任务的编排能力,我们可以使用CompletableFuture提供的API轻松的的编排不同任务的运行顺序。从下面类图可以看到CompletableFuture实现了CompletionStage<T>Future<T>接口。

图片

CompletionStage代表异步计算的一个阶段,它可以是另一个CompletionStage完成时执行一个操作或计算一个值。一个CompletionStage可以是由其他阶段来触发,它完成时也可以触发其他CompletionStage。CompletableFuture实现了CompletionStage接口,使其具备了任务编排的能力

构建CompletableFuture

构建CompletableFuture可以通过下面5个方法

// 创建一个异步有返回值的CompletableFuture,使用ForkJoinPool.commonPool()执行public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) // 创建一个异步有返回值的CompletableFuture,使用自定义线程池执行public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) // 创建一个异步无返回值的CompletableFuture,使用ForkJoinPool.commonPool()执行public static CompletableFuture<Void> runAsync(Runnable runnable)// 创建一个异步无返回值的CompletableFuture,使用自定义线程池执行public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) // 创建一个执行结果是value,已经完成的CompletableFuturepublic static <U> CompletableFuture<U> completedFuture(U value)

虽然CompletableFuture的构造方法是public的,但是还是不建议直接使用new关键字创建CompletableFuture,通过new关键字构建的CompletableFuture是未完备的CompletableFuture,我们可以通过get()方法的源码就可以看出端倪

// java.util.concurrent.CompletableFuture#get()// 调用get()方法时,如果CompletableFuture中result不为空,则会直接返回resultpublic T get() throws InterruptedException, ExecutionException {    Object r;    return reportGet((r = result) == null ? waitingGet(true) : r);}
// java.util.concurrent.CompletableFuture#completedFuture// 通过completedFuture传入的参数会作为CompletableFuture的构造参数public static <U> CompletableFuture<U> completedFuture(U value) {    return new CompletableFuture<U>((value == null) ? NIL : value);}// private构造函数,将入参赋值给resultprivate CompletableFuture(Object r) {    this.result = r;}

从上面源码可以分析出通过new关键字构建的CompletableFuture的未完备指的是没有直接给result赋值或者没有提供Supplier或者Runner,因此还是建议通过上面5个方法构建CompletableFuture

CompletableFuture实现Future能力演示
// 1. 创建线程池ExecutorService executors = Executors.newSingleThreadExecutor();// 2. 创建CompletableFutureCompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {    System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] 开始执行任务");    try {        Thread.sleep(5000);    } catch (InterruptedException e) {        throw new RuntimeException(e);    }    System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] 任务执行完毕");    return "success";}, executors);// 3. 通过get方法阻塞获取执行结果System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] 开始获取执行结果");String result = completableFuture.get();System.out.println(LocalTime.now() + "<---[" + Thread.currentThread().getName() + "] 获取执行结果"+result);// 4. 关闭线程池executors.shutdown();

执行结果如下图所示,主线程调用get()方法后,等待CompletableFuture中的任务执行完毕后才获取到执行结果,可以看出上面代码执行的过程与Future中提供的能力相似

图片

CompletableFuture任务编排

上面CompletableFuture调用get()方法是阻塞的,需要一直等待任务执行完毕才会返回结果,如果我们想要异步回调,可以使用下面的方法

thenApply()

thenApply总共有下面三个方法,它的作用上一个阶段完成时,接收CompletableFuture执行的结果作为参数,执行Function代码,生成一个新返回值的CompletableFuture

图片

  • thenApply(Function<? super T,? extends U> fn)

CompletableFuture执行完毕后,接收CompletableFuture执行的结果作为参数,执行Function,生成一个新CompletableFuture,这两个任务都是在同一个线程池中运行

  • thenApplyAsync(Function<? super T,? extends U> fn)

CompletableFuture执行完毕后,接收CompletableFuture执行的结果作为参数,异步执行Function,生成一个新返回值的CompletableFuture,默认是ForkJoinPool.commonPool()

  • thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

CompletableFuture执行完毕后,接收CompletableFuture执行的结果作为参数,使用指定线程池异步执行Function,生成一个新返回值的CompletableFuture

then后面同步和异步是相对于之前的CompletableFuture任务线程池来说的

thenApply()实战演示代码如下,我们先创建一个任务类Task供后续使用,它的主要任务是打印输出任务情况

public class CommonTask implements Function<String,String>, Consumer<String>,Runnable {    private final long sleepTime;
    private final String taskName;
    public CommonTask(long sleepTime, String taskName) {        this.sleepTime = sleepTime;        this.taskName = taskName;    }
    @Override    public String apply(String result) {        log(result);        return taskName + " success!";    }
    @Override    public void accept(String result) {        log(result);    }
    @Override    public void run() {        log(null);    }
    private void log(String result) {        System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] "+taskName+"执行中,获取到上个任务结果" + result);        try {            Thread.sleep(sleepTime);        } catch (InterruptedException e) {            throw new RuntimeException(e);        }        System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] "+taskName+"执行完毕!");       }}

实现ThreadFactory

public class ThreadFactoryImpl implements ThreadFactory {
    private int i = 0;        private final String namePrefix;
    public ThreadFactoryImpl(String name) {        this.namePrefix = name;    }
    @Override    public Thread newThread(Runnable r) {        Thread thread = new Thread(r);        thread.setName(namePrefix+" - "+(++i));        return thread;    }}

我们先创建一个parent CompletableFuture,然后利用thenApply,创建三个子CompletableFuture,任务依赖关系如下

图片

代码如下

// 1. 创建线程池ExecutorService mainExecutors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("parent"));ExecutorService executors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("apply executors"));
// 2. 创建CompletableFutureCompletableFuture<String> cf = CompletableFuture.supplyAsync(new CommonTask(1000,"parent"),mainExecutors);CompletableFuture<String> cf1 = cf.thenApply(new CommonTask(2500, "thenApply"));CompletableFuture<String> cf2 = cf.thenApplyAsync(new CommonTask(2000, "thenApplyAsync"));CompletableFuture<String> cf3 = cf.thenApplyAsync(new CommonTask(1500, "thenApplyAsync executors"), executors);// 3. 等待cf任务执行CompletableFuture.allOf(cf1, cf2, cf3).join();
// 4. 关闭线程池mainExecutors.shutdown();executors.shutdown();

执行代码,获得如下执行结果,我们可以看出下面三个结论

  • 三个子任务cf1,cf2,cf3开始执行时间与代码中的顺序不一样,

  • thenApply()使用的是parent线程池中的线程执行的

  • thenApplyAsync()如果没有传入指定线程池,默认会使用ForkJoinPool.commonPool

图片

thenAccept()

thenAccept()thenApply()类似,也包含三个方法,不同的是thenAccept()接收的是Consumer任务,下个任务执行完毕后返回CompletableFuture<Void>

图片

  • thenAccept(Consumer<? super T> action)

上个任务完成后,使用上个CcompletableFuture线程池,执行Consumer中的任务

  • thenAcceptAsync(Consumer<? super T> action)

上个任务完成后,异步执行Consumer中的任务,默认是ForkJoinPool.commonPool()

  • thenAcceptAsync(Consumer<? super T> action,Executor executor)

上个任务完成后,使用指定线程池异步执行Consumer中的任务

thenAccept演示代码与thenApply类似,如下所示

// 1. 创建线程池ExecutorService mainExecutors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("parent"));ExecutorService executors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("accept executors"));
// 2. 创建CompletableFutureCompletableFuture<String> cf = CompletableFuture.supplyAsync(new CommonTask(1000,"parent"),mainExecutors);cf.thenAccept(new CommonTask(2500, "thenAccept"));cf.thenAcceptAsync(new CommonTask(2000, "thenAcceptAsync"));cf.thenAcceptAsync(new CommonTask(1500, "thenAcceptAsync executors"), executors);
Thread.sleep(3000);// 3. 关闭线程池mainExecutors.shutdown();executors.shutdown();

执行结果如下

图片

thenRun()

thenRun()也与上面类似,包含三个方法,上个任务执行完毕之后执行Runnable的任务

图片

  • thenRun(Runnable action)

上个任务完成后,使用上个CcompletableFuture线程池,执行Runnable任务

  • thenRunAsync(Runnable action)

上个任务完成后,异步执行Runnable中的任务,默认是ForkJoinPool.commonPool()

  • thenRunAsync(Runnable action,Executor executor)

上个任务完成后,使用指定线程池异步执行Runnable中的任务

thenCombine()

thenCombine()可以合并两个CompletableFuture的执行结果,等待两个任务完成后,将任务结果传给TaskC,再继续执行后续任务

图片

  • thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)

同步合并CompletableFuture执行结果,第一个入参是要合并的CompletionStage,另一个入参是两个CompletableFuture结果合并的BiFunction

  • thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)

异步合并CompletableFuture执行结果,参数与同步的相同

  • thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor)

使用指定的线程池异步合并CompletableFuture执行结果,这个方法相比上面两个增加了线程池作为第三个参数

thenAcceptBoth()

theAcceptBoth()可以看做是无返回值版本的thenCombine()

图片

  • thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)

同步消费CompletableFuture执行结果,第一个入参是要的消费CompletionStage,另一个入参是两个CompletableFuture结果消费的BiConsumer

  • thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)

异步消费CompletableFuture执行结果,参数与同步的相同

  • thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor)

使用指定的线程池异步消费CompletableFuture执行结果,这个方法相比上面两个增加了线程池作为第三个参数

为了演示thenCombine的执行效果,我们通过一段比价的代码来理解thenCombine执行结果,任务如下图所示,首先我们通过CompletableFuture创建两个获取价格任务,然后通过thenCombine比较两者的价格,获取最低价

图片

代码如下所示

 
public static void main(String[] args) throws ExecutionException, InterruptedException {    // 1. 创建线程池    ExecutorService jdExecutors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("JD executors"));    ExecutorService taobaoExecutors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("Taobao executors"));
    // 2. 创建JD CompletableFuture    CompletableFuture<Integer> jdCf = CompletableFuture.supplyAsync(() -> {        log("开始获取JD XX商品价格");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            throw new RuntimeException(e);        }        int price = 100;        log("完成获取JD XX商品价格:"+price);        return price;    }, jdExecutors);    // 3. 创建Taobao CompletableFuture    CompletableFuture<Integer> taobaoCf = CompletableFuture.supplyAsync(() -> {        log("开始获取Taobao XX商品价格");        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            throw new RuntimeException(e);        }        int price = 150;        log("完成获取Taobao XX商品价格" + price);        return price;    },taobaoExecutors);    // 4. 比较两者价格    CompletableFuture<Integer> finnalCf = jdCf.thenCombine(taobaoCf, (jdPrice, taobaoPrice) -> {        log("开始比价,JD price:" + jdPrice + "  taobao Price:" + taobaoPrice);        try {            Thread.sleep(500);        } catch (InterruptedException e) {            throw new RuntimeException(e);        }        int minPrice = Math.min(jdPrice, taobaoPrice);        log("完成比价,最低价格:" + minPrice);        return minPrice;    });
    // 5. 关闭线程池    jdExecutors.shutdown();    taobaoExecutors.shutdown();}
private static void log(String msg) {    System.out.println(LocalTime.now() + "--->["+Thread.currentThread().getName()+"] "+msg);}

执行结果如下所示,在调用thenCombine方法时并没有传入指定线程池,默认采用JD executors

图片

runAfterBoth()

runAfterBoth()可以看做是无入参,无返回值版本的thenCombine()

图片

  • runAfterBoth(CompletionStage<?> other,Runnable action)

等待两个CompletableFuture执行完毕,同步执行另一个Runnable任务

  • runAfterBoth(CompletionStage<?> other,Runnable action)

等待两个CompletableFuture执行完毕,异步执行另一个Runnable任务

  • runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor)

等待两个CompletableFuture执行完毕,使用指定线程池异步执行另一个Runnable任务

runAfterBoth()thenAcceptBoth()类似,可以参考上面例子,这里就不再单独演示了

applyToEither()

两个任务执行,哪个任务执行的快,就会使用那个任务的结果,子任务处理之后生成新的结果

  • applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)

同步执行子任务的方法,子任务使用的线程池与调用applyToEither方法的CF任务使用相同的线程池

这里会有一个问题,同步执行子任务的方法会用哪个线程来执行呢?前面演示的thenCombine()采用的是调用thenCombine()方法CF的线程池

  • applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn)

异步执行子任务的方法,采用ForkJoinPool.commonPool()线程池

  • applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor)

使用指定线程池异步执行子任务

acceptEither()

两个任务执行,哪个任务执行的快,就会消费那个结果,不会有返回值

  • acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)

同步消费子任务的方法

  • acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)

异步消费子任务的方法,采用ForkJoinPool.commonPool()线程池

  • acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor)

使用指定线程池异步消费子任务的方法

我们依然采用上面比价的例子演示,分别创建jdCf和taobaoCf两个获取价格的CompletableFuture,然后我们使用acceptEither方法打印出先获取到的价格

public static void main(String[] args) throws ExecutionException, InterruptedException {    // 1. 创建线程池    ExecutorService jdExecutors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("JD executors"));    ExecutorService taobaoExecutors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("Taobao executors"));
    // 2. 创建JD CompletableFuture    CompletableFuture<Integer> jdCf = CompletableFuture.supplyAsync(() -> {        log("开始获取JD XX商品价格");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            throw new RuntimeException(e);        }        int price = 100;        log("完成获取JD XX商品价格:"+price);        return price;    }, jdExecutors);    // 3. 创建Taobao CompletableFuture    CompletableFuture<Integer> taobaoCf = CompletableFuture.supplyAsync(() -> {        log("开始获取Taobao XX商品价格");        try {            Thread.sleep(3000);        } catch (InterruptedException e) {            throw new RuntimeException(e);        }        int price = 150;        log("完成获取Taobao XX商品价格" + price);        return price;    },taobaoExecutors);    // 4. 打印出最先获取的价格    jdCf.acceptEither(taobaoCf, (fastResult) -> {        log("完成价格获取,更快获取的价格为:" + fastResult);    });
    // 5. 关闭线程池    jdExecutors.shutdown();    taobaoExecutors.shutdown();}
private static void log(String msg) {    System.out.println(LocalTime.now() + "--->["+Thread.currentThread().getName()+"] "+msg);}

演示结果如下所示

图片

从上面结果可以看到,执行子任务的线程池是先执行完任务的JD executors,那么我们调整上面任务的时间,将获取taobao价格的时间设置为1000ms,这样会先执行完taobaoCf,执行结果如下所示,执行子任务的线程池又换成了Taobao executors

图片

从上面的结果我们可以得到结论,同步执行的acceptEither()会采用先执行完任务CompletableFuture的线程池执行子任务

runAfterEither()

两个任务执行,只要任意一个任务执行完毕,就会开始执行下一个任务(Runnable)

  • runAfterEither(CompletionStage<?> other,Runnable action)

只要一个任务执行完毕,则同步执行子任务

  • runAfterEitherAsync(CompletionStage<?> other,Runnable action)

采用ForkJoinPool.commonPool()线程池执行子任务

  • runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor)

使用指定线程池执行子任务

whenComplete()

whenComplete()是当任务执行完毕之后的回调方法,会将执行结果或者任务运行期间的异常传递给回调方法,如果正常执行,则异常为空,whenComplete()与上面类似,有1个同步方法,2个异步方法

  • whenComplete(BiConsumer<? super T, ? super Throwable> action)

CompletableFuture任务完成后,同步执行whenComplete()回调

  • whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)

采用ForkJoinPool.commonPool()线程池,异步执行回调

  • whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)

指定线程池异步执行回调

handle()

handle()whenComplete()方法类似,也是当任务任务完成时执行回调方法,区别是whenComplete()没有返回值,handle()有返回值

  • handle(BiFunction<? super T, Throwable, ? extends U> fn)

同步handle()

  • handleAsync(BiFunction<? super T, Throwable, ? extends U> fn)

采用ForkJoinPool.commonPool()线程池,回调handle()

  • handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,Executor executor)

指定线程池异步回调handle()

exceptionally()

CompletableFuture任务完成时如果发生异常,则后续任务不会执行,CompletableFuture会将异常传给exceptionally()处理

  • exceptionally(Function<Throwable, ? extends T> fn)

allOf()

返回一个新的CompletableFuture,当所有给定的ComplettableFuture完成时,该新的ComplextableFuture已完成。如果任何给定的CompletableFutures异常完成,那么返回的ComplettableFuture也会这样做,CompletionException将此异常作为其原因。

CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)

allOf()使用场景是多个任务全部执行完毕后,在执行后续任务,例如我们有三个任务taskA,taskB,taskC,要在这三个任务执行完之后再执行finalTask任务

图片

代码如下所示

// 1.创建三个taskA,taskB,taskC completableFutureCompletableFuture<String> taskACf = CompletableFuture.supplyAsync(new CommonTask(1000, "taskA"));CompletableFuture<String> taskBCf = CompletableFuture.supplyAsync(new CommonTask(2000, "taskB"));CompletableFuture<String> taskCCf = CompletableFuture.supplyAsync(new CommonTask(3000, "taskC"));// 2. 使用allOfCompletableFuture<Void> allOfCf = CompletableFuture.allOf(taskACf, taskBCf, taskCCf);// 3. taskA,taskB,taskC执行完毕之后执行finalTaskCompletableFuture<Void> finalCf = allOfCf.thenRun(new CommonTask(2000, "finalTask"));finalCf.get();

执行结果如下所示,可以看到taskA,tackB,taskC执行耗时不同,使用allOf()合并后,finalTask会等待taskA,taskB,taskC执行完毕后再执行

图片

假设taskA,tackB,taskC中只要有一个任务执行失败,这三个任务相互不会影响,而finalTask则不会执行,还是上面例子,假设taskB执行过程中抛出异常,我们可以观察finalTask执行情况,代码如下所示

// 1.创建三个不同的CF任务CompletableFuture<String> taskACf = CompletableFuture.supplyAsync(new CommonTask(1000, "taskA"));CompletableFuture<String> taskBCf = CompletableFuture.supplyAsync(()->{    System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] taskB执行中");    try {        Thread.sleep(1500);    } catch (InterruptedException e) {        throw new RuntimeException(e);    }    throw new RuntimeException("任务B执行失败");});CompletableFuture<String> taskCCf = CompletableFuture.supplyAsync(new CommonTask(3000, "taskC"));
// 2. 使用allOfCompletableFuture<Void> allOfCf = CompletableFuture.allOf(taskACf, taskBCf, taskCCf);// 3. taskA,taskB,taskC执行完毕之后执行finalTaskCompletableFuture<Void> finalCf = allOfCf.thenRun(new CommonTask(2000, "finalTask"))        .exceptionally((e)->{            System.out.println("final捕获到异常:"+e);            return null;        });finalCf.get();

执行结果如下,我们可以看到finalTask并没有执行,exceptionally()捕获到taskB抛出的异常

图片

其实还有更极端的场景是假设多个任务都抛出异常了,调用allOf()CompletableFuture会捕获哪个任务抛出的异常呢?大家可以实践一下

anyOf()

了解了allOf()方法,我们再来学习anyOf()就会简单很多,anyOf()就是上游任务只要有一个任务执行完了,就会执行后续任务,还是拿上面的例子taskA,tackB,taskC只要有一个任务执行完毕之后就执行finalTask

anyOf()allOf()不同的是anyOf()方法获取的CompletableFuture<Object>会返回最快执行完成任务的结果

// 1.创建三个不同的CF任务CompletableFuture<String> taskACf = CompletableFuture.supplyAsync(new CommonTask(1000, "taskA"));CompletableFuture<String> taskBCf = CompletableFuture.supplyAsync(new CommonTask(2000, "taskB"));CompletableFuture<String> taskCCf = CompletableFuture.supplyAsync(new CommonTask(3000, "taskC"));
// 2. 使用anyOfCompletableFuture<Object> anyOfCf = CompletableFuture.anyOf(taskACf, taskBCf, taskCCf);// 3. taskA,taskB,taskC只要一个任务执行完毕之后,就执行anyOfCompletableFuture<Void> finalCf = anyOfCf.thenAccept((r)->{    System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] "+"finalTask执行中,获取到上个任务结果" + r);    try {        Thread.sleep(1000);    } catch (InterruptedException e) {        throw new RuntimeException(e);    }    System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] "+"finalTask执行完毕!");});finalCf.get();

执行结果如下,taskA最快执行完毕,taskA执行完毕之后,finalTask立即开始执行了

图片

anyOf()allOf()处理异常方式对比

anyOf()处理异常的方式与allOf()并不相同,在还没有任务完成前其中一个发生异常,anyOf()中的任务并不会相互影响,后续任务不会执行,异常将会抛给exceptionally()处理。如果已经有任务完成了,后续任务如果发生异常,不会对后续任务有影响,后续任务还可以正常执行。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值