CompletableFuture异步编排(附:业务伪代码案例)

目录

一、概述

 二、创建异步任务

 三、完成时回调方法

 四、线程串行化方法

 五、两任务组合 -- 都要完成

 六、两任务组合 -- 一个完成

 七、多任务组合

 八、业务场景的使用


一、概述

CompletableFuture是jdk8的新特性。CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步会点、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利。

 二、创建异步任务

1.Async结尾的表示异步执行任务,supply开头的表示有返回值,run无返回值

// 带返回值异步请求,默认线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
 
// 带返回值的异步请求,可以自定义线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

// 不带返回值的异步请求,默认线程池
public static CompletableFuture<Void> runAsync(Runnable runnable)
 
// 不带返回值的异步请求,可以自定义线程池
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)

 2.测试

package com.atguigu.java;

import org.junit.Test;

import java.util.concurrent.*;

public class ThreadTest {
    public static ThreadPoolExecutor executor= new ThreadPoolExecutor(
            5,
            10,
            10,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());


    //测试创建有返回值的异步任务
    @Test
    public void test1() throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            System.out.println("当前线程计算结束" + i);
            return i;
        }, executor);

        //等待任务执行完成
        System.out.println("结果->" + Future1.get());
        System.out.println("main...end...");
    }

    //测试创建有返回值的异步任务
    @Test
    public void test2() throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        CompletableFuture<Void> Future1 = CompletableFuture.runAsync(() -> {
            int i = 10 / 2;
            System.out.println("当前线程计算结束" + i);
        }, executor);

        //等待任务执行完成
        System.out.println("结果->" + Future1.get());
        System.out.println("main...end...");
    }
}

 3.获取任务结果的方法

// 如果完成则返回结果,否则就抛出具体的异常
public T get() throws InterruptedException, ExecutionException 
 
// 最大时间等待返回结果,否则就抛出具体异常
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
 
// 完成时返回结果值,否则抛出unchecked异常。为了更好地符合通用函数形式的使用,如果完成此 CompletableFuture所涉及的计算引发异常,则此方法将引发unchecked异常并将底层异常作为其原因
public T join()
 
// 如果完成则返回结果值(或抛出任何遇到的异常),否则返回给定的 valueIfAbsent。
public T getNow(T valueIfAbsent)
 
// 如果任务没有完成,返回的值设置为给定值
public boolean complete(T value)
 
// 如果任务没有完成,就抛出给定异常
public boolean completeExceptionally(Throwable ex) 

 三、完成时回调方法

1.whenComplete可以处理正常返回的结果和异常信息

exceptionally 处理异常信息并支持返回

public CompletableFuture<T> whenComplete(
        BiConsumer<? super T, ? super Throwable> action) 
//void accept(T t, U u);

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

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

public CompletableFuture<T> exceptionally(
        Function<Throwable, ? extends T> fn)
//R apply(T t);

测试

 //测试完成时回调方法及异常处理返回
    @Test
    public void test3() throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 0;
            System.out.println("当前线程计算结束" + i);
            return i;
        }, executor).whenCompleteAsync((result,exception)->{
            System.out.println("上个任务结果:" + result);
            System.out.println("上个任务抛出异常:" + exception);
            System.out.println(Thread.currentThread() + " test3 do something....");
        },executor).exceptionally(exception ->{
            return 0;
        });
        System.out.println( Future1.get());
        //等待任务执行完成
        System.out.println("main...end...");
    }

2.handle和handleAsync

 跟whenComplete基本一致,区别在于handle的回调方法有返回值可以改变返回值。

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


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


public <U> CompletableFuture<U> handleAsync(
        BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)
//R apply(T t, U u);

测试:

//测试handle方法
    @Test
    public void test4() throws Exception {
        System.out.println("main...start...");
        CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 0;
            System.out.println("当前线程计算结束" + i);
            return i;
        }, executor).handleAsync((result,exception)->{
            //有结果直接返回结果 有异常返回0
            if (result != null){
                return result;
            }
            if (exception != null){
                return 0;
            }
            return 0;
        },executor);
        System.out.println( Future1.get());
        //等待任务执行完成
        System.out.println("main...end...");
    }

 四、线程串行化方法

1.thenRun方法:只要上面点的方法前执行完成,就开始执行thenRun后的方法

不会拿上个任务的返回值 并且后面的任务也没有返回值

public CompletableFuture<Void> thenRun(Runnable action) 

public CompletableFuture<Void> thenRunAsync(Runnable action) 

public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor) 

 2.thenAccept方法:会拿上个任务的返回结果进行消费,但是后面的任务,无返回结果

public CompletableFuture<Void> thenAccept(Consumer<? super T> action)

public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) 

public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor) 

//void accept(T t);

 3.thenApply方法:会拿上个任务的返回结果进行消费,并且后面的任务,会返回结果

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

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

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

//R apply(T t);

 测试:

 //测试thenRun方法
    @Test
    public void test5() throws Exception {
        System.out.println("main...start...");
        CompletableFuture<Void> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            System.out.println("当前线程计算结束" + i);
            return i;
        }, executor).thenRunAsync(()->{
            System.out.println("任务2启动了");
        }, executor);
        System.out.println( Future1.get());
        //等待任务执行完成
        System.out.println("main...end...");
    }


    //测试thenAccept方法
    @Test
    public void test6() throws Exception {
        System.out.println("main...start...");
        CompletableFuture<Void> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            System.out.println("当前线程计算结束" + i);
            return i;
        }, executor).thenAcceptAsync((result)->{
            System.out.println("任务2启动了,拿到任务1的计算结果为"+result);
        }, executor);
        System.out.println( Future1.get());
        //等待任务执行完成
        System.out.println("main...end...");
    }

    //测试thenApply方法
    @Test
    public void test7() throws Exception {
        System.out.println("main...start...");
        CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            System.out.println("当前线程计算结束" + i);
            return i;
        }, executor).thenApplyAsync((result)->{
            System.out.println("任务2启动了,拿到任务1的计算结果为"+result);
            //对任务1的结果再加上1
            return result+1;
        }, executor);
        System.out.println("最后的结果为"+Future1.get());
        //等待任务执行完成
        System.out.println("main...end...");
    }

 五、两任务组合 -- 都要完成

这三个方法都是将两个CompletableFuture组合起来处理,只有两个任务都正常完成时,才进行下阶段任务。

区别:

thenCombine会将两个任务的执行结果作为所提供函数的参数,且该方法有返回值;

thenAcceptBoth同样将两个任务的执行结果作为方法入参,但是无返回值;

runAfterBoth没有入参,也没有返回值。注意两个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果。
 

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

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

public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,
        BiFunction<? super T,? super U,? extends V> fn, Executor executor)
// R apply(T t, U u);
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,
        BiConsumer<? super T, ? super U> action)

public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
        BiConsumer<? super T, ? super U> action) 

public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
        BiConsumer<? super T, ? super U> action, Executor executor) 
//void accept(T t, U u);
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action) 

public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action)

public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,
                                                     Executor executor) 

测试:

//测试runAfterBoth方法
    @Test
    public void test8() throws Exception {
        System.out.println("main...start...");
        CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            System.out.println("任务1启动");
            return i;
        }, executor);

        CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            System.out.println("任务2启动");
            return i;
        }, executor);

        CompletableFuture<Void> Future3 = Future1.runAfterBothAsync(Future2, () -> {
            System.out.println("任务1和2执行完毕 启动任务3");
        }, executor);
        //等待任务执行完成
        System.out.println("任务3结果->" + Future3.get());
        System.out.println("main...end...");
    }


    //测试thenAcceptBoth方法
    @Test
    public void test9() throws Exception {
        System.out.println("main...start...");
        CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            System.out.println("任务1启动");
            return i;
        }, executor);

        CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 5;
            System.out.println("任务2启动");
            return i;
        }, executor);

        CompletableFuture<Void> Future3 = Future1.thenAcceptBothAsync(Future2, (result1,result2) -> {
            System.out.println("任务1的结果"+result1+"~~~~~ 任务2的结果" + result2);
            System.out.println("任务1和2执行完毕 启动任务3");
        }, executor);
        //等待任务执行完成
        System.out.println("任务3结果->" + Future3.get());
        System.out.println("main...end...");
    }


    //测试thenCombine方法
    @Test
    public void test10() throws Exception {
        System.out.println("main...start...");
        CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            System.out.println("任务1启动");
            return i;
        }, executor);

        CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 5;
            System.out.println("任务2启动");
            return i;
        }, executor);

        CompletableFuture<Integer> Future3= Future1.thenCombineAsync(Future2, (result1, result2) -> {
            System.out.println("任务1和2执行完毕 启动任务3");
            return result1+result2;
        }, executor);
        //等待任务执行完成
        System.out.println("任务3结果->" + Future3.get());
        System.out.println("main...end...");
    }

 六、两任务组合 -- 一个完成

这三个方法和上面一样也是将两个CompletableFuture组合起来处理,当有一个任务正常完成时,就会进行下阶段任务。

区别:

applyToEither会将已经完成任务的执行结果作为所提供函数的参数,且该方法有返回值;acceptEither同样将已经完成任务的执行结果作为方法入参,但是无返回值;

runAfterEither没有入参,也没有返回值。

测试

//测试runAfterEitherh方法
    @Test
    public void test11() throws Exception {
        System.out.println("main...start...");
        CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            System.out.println("任务1启动");
            return i;
        }, executor);

        CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2启动");
            return i;
        }, executor);

        CompletableFuture<Void> Future3 = Future1.runAfterEitherAsync(Future2, () -> {
            System.out.println("任务1和2有一个执行完毕 启动任务3");
        }, executor);
        //等待任务执行完成
        System.out.println("任务3结果->" + Future3.get());
        System.out.println("main...end...");
    }


    //测试acceptEither方法
    @Test
    public void test12() throws Exception {
        System.out.println("main...start...");
        CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            System.out.println("任务1启动");
            return i;
        }, executor);

        CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 5;
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2启动");
            return i;
        }, executor);

        CompletableFuture<Void> Future3 = Future1.acceptEitherAsync(Future2, (result) -> {
            System.out.println("最先完成任务的结果"+result);
            System.out.println("任务1和2有一个执行完毕 启动任务3");
        }, executor);
        //等待任务执行完成
        System.out.println("任务3结果->" + Future3.get());
        System.out.println("main...end...");
    }


    //测试applyToEither方法
    @Test
    public void test13() throws Exception {
        System.out.println("main...start...");
        CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            System.out.println("任务1启动");
            return i;
        }, executor);

        CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 5;
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2启动");
            return i;
        }, executor);

        CompletableFuture<Integer> Future3= Future1.applyToEitherAsync(Future2, (result) -> {
            System.out.println("最先完成任务的结果"+result);
            System.out.println("任务1和2有一个执行完毕 启动任务3");
            return result;
        }, executor);
        //等待任务执行完成
        System.out.println("任务3结果->" + Future3.get());
        System.out.println("main...end...");
    }

 七、多任务组合

1.allof方法:等待所有任务完成  返回的是null

2.anyof:只要有一个任务完成 可以返回先完成那个任务的结果

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

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
//测试allof方法
    @Test
    public void test14() throws Exception {
        System.out.println("main...start...");
        CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            System.out.println("任务1启动");
            return i;
        }, executor);

        CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 5;
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2启动");
            return i;
        }, executor);

        CompletableFuture<Integer> Future3 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 5;
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务3启动");
            return i;
        }, executor);

        CompletableFuture<Void> allOf = CompletableFuture.allOf(Future1, Future2, Future2);

        //等待所有任务都完成
        System.out.println(allOf.get());
    }


    //测试anyof方法
    @Test
    public void test15() throws Exception {
        System.out.println("main...start...");
        CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            System.out.println("任务1启动");
            return i;
        }, executor);

        CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 5;
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2启动");
            return i;
        }, executor);

        CompletableFuture<Integer> Future3 = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 5;
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务3启动");
            return i;
        }, executor);

        CompletableFuture<Object> anyof = CompletableFuture.anyOf(Future1, Future2, Future2);

        //等待一个任务完成
        System.out.println(anyof.get());
    }

 八、业务场景的使用

案例来自尚硅谷

 假设要求:

第一,要先拿到商品的基本信息 基本信息里面有 销售id 和 销售规格id

第二,拿到商品基本信息后 可以 根据商品基本信息 异步去获取 促销、销售属性、规格参数等信息

第三,图片信息和商品基本没有上下关联关系 可以同时异步获取

第四,所以信息全部查询完成后一起返回

伪代码:

 @Test
    public void test16() throws ExecutionException, InterruptedException {
        //所有信息的汇总
        SkuiVo skuiVo = new SkuiVo();
        //查询基本信息(带返回值的异步)
        CompletableFuture<Skuinfo> infoFuture = CompletableFuture.supplyAsync(() -> {
                    //假设查询到了商品基本信息
                    Skuinfo skuinfo = new Skuinfo();
                    skuinfo.setSpuId("1");
                    skuinfo.setSpuId("2");
                    skuinfo.setGuiId("3");
                    skuiVo.setSkuinfo(skuinfo);
                    return skuinfo;
                }
                , executor);

        //查到基本信息后 异步同时去查 促销信息 规格信息 销售属性信息
        //拿到查基本信息任务的返回值 任务本身无需返回值
        CompletableFuture<Void> saleCxFuture = infoFuture.thenAcceptAsync((res) -> {
            String spuId = res.getSpuId();
            //拿到商品的销售id后查促销信息
            skuiVo.setSaleCx("促销信息");
        }, executor);

        CompletableFuture<Void> saleSxFuture = infoFuture.thenAcceptAsync((res) -> {
            String spuId = res.getSpuId();
            //拿到商品的销售id后查销售属性
            skuiVo.setSaleSx("销售属性信息");
        }, executor);


        CompletableFuture<Void> saleGgFuture = infoFuture.thenAcceptAsync((res) -> {
            String spuId = res.getSpuId();
            String guiId = res.getGuiId();
            //拿到商品的销售id和规格id 查商品规格信息
            skuiVo.setSaleGg("商品规格信息");
        }, executor);

        //查基本信息的时候 同时异步 也根据商品id 查商品图片信息
        //这个任务不需要返回值
        CompletableFuture<Void> imageFuture= CompletableFuture.runAsync(() -> {
            //查商品图片信息
            skuiVo.setImages("商品图片信息");
        }, executor);

        //等待所有任务都完成
        CompletableFuture.allOf(saleCxFuture,saleSxFuture,saleGgFuture,imageFuture).get();

        System.out.println(skuiVo);
    }

测试结果:

参考:

1.尚硅谷谷粒商城教学

2.CompletableFuture使用详解(全网看这一篇就行)_代码搬运工阿新的博客-CSDN博客

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小瞿码上有

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值