CompletableFuture 的知识与运用

CompletableFuture的概念

什么是CompletableFuture?

可完成的Future,不同于异步的Future依赖轮询获取结果,CompletableFuture可以在不同的阶段获取异步线程的执行结果,或者根据丰富的链式回调函数完成结果的多重转换

从接口的实现来看,CompletableFuture实现了Future以及CompletionStage两个接口,CompletionStage接口中支持多种方法,包括带有返回值的和不带返回值的,下面将对这些方法进行解释:

`CompletionStage<T>`接口定义了可能异步计算的一个阶段,当一个`CompletionStage`完成时,它可以触发另一个`CompletionStage`。以下是一些常用的方法:

1. `thenApply(Function<? super T,? extends U> fn)`: 当前阶段正常完成后,将结果作为参数传递给函数`fn`,并返回一个新的`CompletionStage`来表示函数`fn`的结果。

2. `thenAccept(Consumer<? super T> action)`: 当前阶段正常完成后,将结果作为参数传递给消费者`action`,并返回一个新的`CompletionStage`来表示`action`的完成。

3. `thenRun(Runnable action)`: 当前阶段正常完成后,运行给定的动作,并返回一个新的`CompletionStage`来表示`action`的完成。

4. `thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)`: 当前阶段和另一个阶段都正常完成后,将这两个阶段的结果作为参数传递给函数`fn`,并返回一个新的`CompletionStage`来表示`fn`的结果。

5. `thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)`: 当前阶段和另一个阶段都正常完成后,将这两个阶段的结果作为参数传递给消费者`action`,并返回一个新的`CompletionStage`来表示`action`的完成。

6. `applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)`: 当前阶段和另一个阶段中任意一个先正常完成,就将该阶段的结果作为参数传递给函数`fn`,并返回一个新的`CompletionStage`来表示`fn`的结果。

7. `acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)`: 当前阶段和另一个阶段中任意一个先正常完成,就将该阶段的结果作为参数传递给消费者`action`,并返回一个新的`CompletionStage`来表示`action`的完成。

8. `runAfterEither(CompletionStage<?> other, Runnable action)`: 当前阶段和另一个阶段中任意一个先正常完成,就运行给定的动作,并返回一个新的`CompletionStage`来表示`action`的完成。

9. `runAfterBoth(CompletionStage<?> other, Runnable action)`: 当前阶段和另一个阶段都正常完成后,运行给定的动作,并返回一个新的`CompletionStage`来表示`action`的完成。

10. `whenComplete(BiConsumer<? super T, ? super Throwable> action)`: 当前阶段完成时(无论正常还是异常),将结果(如果正常完成)或异常(如果异常完成)作为参数传递给消费者`action`,并返回一个新的`CompletionStage`来表示`action`的完成。

11. `handle(BiFunction<? super T, Throwable, ? extends U> fn)`: 当前阶段完成时(无论正常还是异常),将结果(如果正常完成)或异常(如果异常完成)作为参数传递给函数`fn`,并返回一个新的`CompletionStage`来表示`fn`的结果。

CompletableFuture异步原理

CompletableFuture
result
stack

CompletableFuture 中CompletationStage的运用

supplyAsync

简言之就是带有返回值的异步调用方法,其中有两个重载方法,可以根据是否传入线程池对象,决定使用内置的Fork线程池还是自定义的线程池

//使用默认内置线程池ForkJoinPool.commonPool(),根据supplier构建执行任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
//自定义线程,根据supplier构建执行任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
//定义线程池
 static
    ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 1, TimeUnit.HOURS, new LinkedBlockingDeque<>(),
            new ThreadPoolExecutor.AbortPolicy());

主线程定义completableFuture的方法,使用我们上面定义的线程池,内部打印Thread.sleep 模拟线程执行的过程

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(
                () -> {
                    try{
                        System.out.println("current thread is : "+ Thread.currentThread().getName());
                        Thread.sleep(
                                2000
                        );
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                  return "success";
                }
        , executor).exceptionally(e -> {
            e.printStackTrace();
            return "error";
        });

thenCombine

顾名思义,属于组合多个future结果的异步回调方法,观察其中的原理方法,是一个双输入的函数和一个前一阶段返回结果的CompletationStage

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

BiFunction<? super T,? super U,? extends V> fn是一个函数式接口,它接收两个参数,一个类型为T,一个类型为U,并返回一个类型为V的结果。

这里的? super T? super U表示参数类型可以是TU的任何父类型,? extends V表示返回类型可以是V的任何子类型

可以给出一个这个类型的调用示例:

BiFunction<Integer, Integer, String> biFunction = (num1, num2) -> "Result: " + (num1 + num2);
System.out.println(biFunction.apply(2, 3)); 
//整合future1和future2的结果,给到future3,并将其打印,可以看到多线程异步结果的组合
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(
                    () -> {
                        try{
                            System.out.println("current thread is : "+ Thread.currentThread().getName());
                            Thread.sleep(
                                    2000
                            );
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                        return " Wang_Zimeng";
                    }
                    , executor).exceptionally(e -> {
                e.printStackTrace();
                return "error";
            });

            CompletableFuture<String> future3 = future1.thenCombine(future2, (result1, result2) -> {
                String str1 = result1 + " "+ result2;
                System.out.println(str1 +" "+ Thread.currentThread().getName());
                return  str1;
            });

thenAcceptAsync

CompletableFuture<Void> future3 = future1.thenAcceptBoth(future2, (res1,res2) -> {
    System.out.println(res1 + res2);
});

无返回值,主要用于打印结果

CompletableFuture的应用场景

case 1 执行较为耗时的代码,可以使用异步的CompletableFuture或者FutureTask,如果后继的代码对于当前异步的结果有依赖,可以使用CompletableFuture获取,基于非阻塞式的返回

case2 对大批量数据相同操作的分片异步处理场景

假设我们的应用跑的是城市维度的数据,对于不同的城市希望进行分片异步执行,首先给出分片的代码:

public static <T> List<List<T>> getPartition(List<T> list,int k){
        List<List<T>> res = new ArrayList<>();
        if(list.size() <= k){
            res.add(list);
            return res;
        }
        int count = 0;
        while(count < list.size()){
            List<T> subList = list.subList(count, Math.min(count+k, list.size()));
            res.add(subList);
            count += k;
        }
        return res;
    }

然后我们将一维列表通过分片变成二维,进行列表的异步执行,因为每一个分片需要执行一个异步CompletableFuture,所以我们申请futures数组,长度等同于一维数组分片后的partition数量

然后基于这个分组,进行数据的计算(supplyAsync)以及对上一步结果的再处理(thenApplyAsyn)

最终,主线程使用CompletableFuture.allof(futures).join,保证主线程必须在所有future执行完毕后,才可以进行输出展示和打印

 List<List<Integer>> res = PartitionUtil.getPartition(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0), 3);

        CompletableFuture[] futures = res.stream().map(partiotionSlice ->
                    CompletableFuture.supplyAsync(() -> {
                        try {
                            //模拟程序调用
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        return "success" + partiotionSlice.toString();
                    }, executor).thenApplyAsync(
                            (r) -> {
                                System.out.println(" Wang_Zimeng");
                                return r + "wangzimeng";
                            }, executor
                    )).toArray(CompletableFuture[]::new);

                CompletableFuture.allOf(futures).join();
        for (CompletableFuture future : futures) {
            Object result = future.join();
            // 处理结果
            System.out.println("CompletableFuturesRes ="+   result);
        }

当然,如果是要做数据结果的收集的话,可以使用一个List,作为结果的addAll,最后在joins之后可以保证list的结果是所有获取的futures的终态stage的计算结果

  • 37
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值