CompletableFuture使用

CompletableFuture使用

在之前多线程的文章中会遇到一些问题:

 				while(!fs.isDone());//Future返回如果没有完成,则一直循环等待,直到Future返回完成
                System.out.println(fs.get());    //打印各个线程(任务)执行的结果

1)若Future返回没有完成,当前主线程是堵塞的,主线程业务会一直等着。

2)轮询调用isDone,不优雅。

解决上述问题并应对更多的需要:

1)很多个异步线程执行时间不一致,主线程业务不需要一直等着,可能只等最快的线程执行完或者最重要的任务执行完,或者等待固定的时间(比如几秒钟,防止调用超时),至于没返回结果的线程我就用默认值代替.

2)两个异步任务之间执行独立,但是第二个依赖第一个的执行结果。

CompletableFuture提供了函数式编程能力,使代码更加美观优雅,而且可以通过回调的方式计算处理结果,对异常处理也有了更好的处理手段。

CompletableFuture类四个静态方法执行异步任务:

  • 执行有返回值的任务了,方法的入参如果没有传入Executor对象将会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。在实际使用中,一般使用自己创建的线程池对象来作为参数传入使用,提高线程速度。
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier){..}

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor){..}
  • 用于执行没有返回值的任务,因为它的入参是Runnable对象。
public static CompletableFuture<Void> runAsync(Runnable runnable){..}

public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)){..}

获取执行结果的几个方法

  • Future中的实现方式,get()会堵塞当前的线程,如果执行线程迟迟没有返回数据,get()会一直等待下去。抛出的是经过检查的异常,ExecutionException, InterruptedException 需要用户手动处理(抛出或者 try catch)。
V get();
  • 可以设置等待的时间。
V get(long timeout,Timeout unit);
  • 当有了返回结果时会正常返回,如果异步线程抛了异常会返回设置的默认值。
T getNow(T defaultValue);
  • 功能和 get() 方法是一样的,都是阻塞获取值,它们的区别在于 join() 抛出的是 unchecked Exception,不会强制开发者抛出,会将异常包装成CompletionException异常 /CancellationException异常,但是本质原因还是代码内存在的真正的异常。Exception in thread “main” java.util.concurrent.CompletionException: java.lang.ArithmeticException:
T join();

thenAccept()

  • 功能:当前任务的执行结果可以作为下一任务的输入参数,无返回值。
public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);
  • 使用场景:执行任务A,待任务A正常返回之后,用A的返回值执行任务B。
    public static void main(String[] args) {
        CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
            return "A";
        });
        futureA.thenAccept(A -> {
            System.out.println("打印依赖的线程返回值-"+ A);
        });
    }

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
  • 使用场景:执行任务A,待任务A正常返回之后,用A的返回值执行任务B,下一个任务的执行依赖上一个任务的结果,每个任务都有输入和输出。
public class MyCompletableFuture {
    public static void main(String[] args) {
        CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
            return "A";
        });
        CompletableFuture<String> futureB = futureA.thenApply(A -> {
            System.out.println("打印依赖的线程返回值-"+ A);
            return "B";
        });
    }
}
  • 以上功能实现,我们可以先调用future.join()先得到任务A的返回值,然后再拿返回值做入参去执行任务B,而thenApply的存在就在于简化了这一步,我们不必因为等待一个计算完成而一直阻塞着调用线程,而是告诉CompletableFuture何时执行完就进行下一步,将多个任务串联起来了。

thenRun(…)

  • 功能:不关心上一步的计算结果,上一步执行完以后执行下一个操作。
public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
  • 场景:先执行任务A,任务A执行完以后执行任务B,任务B不接受任务A的返回值(不管A有没有返回值),无返回值。
/**
* @creater keke
* @time 2021/1/22 15:29
* @description
*/
public class MyCompletableFuture2 {
   public static void main(String[] args) {
       CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
           System.out.println("执行任务A");
           return "A";
       });
       futureA.thenRun(() -> System.out.println("执行任务B"));
   }
}

thenCombine(…) runAfterBoth(…)

  • 功能:结合两个CompletionStage的结果,进行转化后返回
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)
  • 场景:future1 和future2 相互独立,当都执行完毕的时候,使用方法:thenCombine(…)将两个任务的结果一块交给 thenCombine 来处理。

   public static void main(String[] args) throws ExecutionException, InterruptedException {

       CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "hello");
       CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "world");
       CompletableFuture<String> result = future1.thenCombine(future2, (t, u) ->
               t + " " + u
       );
       System.out.println(result.get());
   }

thenAcceptBoth(…)

  • 功能:当两个CompletionStage都执行完成后,把结果一块交给thenAcceptBoth来进行消耗。
public class MyThenAcceptBoth {
   public static void main(String[] args) {
       CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 1);
       CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 3);
       future1.thenAcceptBoth(future2, (t, u) ->
               System.out.println(t * u));
   }
}

applyToEither(…) acceptEither(…) runAfterEither(…)

  • 功能:两个CompletionStage,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的转化操作。
public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other,Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn,Executor executor);
  • 场景:查询结果有两种方式蓝牙和网络,两种方式查询速度不一致,我们希望哪个先返回就用那个的返回值。
public class MyApplyToEither {
    public static void main(String[] args) {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 5);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() ->{
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 7;
        });
        CompletableFuture<Integer> future3 = future1.applyToEither(future2,result -> result);
        System.out.println(future3.join());//输出:5
    }
}

exceptionally(…)

  • 功能:当运行出现异常时,调用该方法可进行一些补偿操作,如设置默认值.
public CompletionStage<T> exceptionally(Function<Throwable, ? extends T> fn);
  • 场景:异步执行任务A获取结果,如果任务A执行过程中抛出异常,则使用默认值"请重试!"返回.
public class MyExceptionally {
    public static void main(String[] args) {
        CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "异常:" + 100 / 0)
                .thenApply(s -> "f1 result:" + s)
                .exceptionally(e -> {
                    System.out.println(e.getMessage()); //java.lang.ArithmeticException: / by zero
                    return "f1 result: 请重试!";
                });

        System.out.println(f1.join());//输出"f1 result: 请重试!"
        CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "成功!" )
                .thenApply(s -> "f2 result:" + s)
                .exceptionally(e -> {
                    System.out.println(e.getMessage()); //java.lang.ArithmeticException: / by zero
                    return "f2 result: 请重试!";
                });

        System.out.println(f2.join());//输出"f2 result: 成功!"
    }
}

whenComplete(…)

  • 功能::当CompletableFuture的结果成功执行,或者抛出异常的时候,都可以进入whenComplete方法执行。
public CompletionStage<T> whenComplete(BiConsumer<? super T, ? super Throwable> action);
public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action);
public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action,Executor executor);
  • 功能:当CompletableFuture的计算结果完成,或者抛出异常的时候,都可以进入whenComplete方法执行.
public class MyWhenComplete {
    public static void main(String[] args) {
        CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "结果是:" + 100 / 0)
                .thenApply(s -> "正确的结果是:" + s)
                .whenComplete((s, e) -> {
                    if (!Objects.isNull(s)) {
                        System.out.println(s);//未执行
                    }
                    if (Objects.isNull(e)) {
                        System.out.println(s);//未执行
                    } else {
                        System.out.println(e.getMessage());//java.lang.ArithmeticException: / by zero
                    }
                })
                .exceptionally(e -> {
                            System.out.println(e.getMessage());
                            return "请重试!";
                        }
                );
        System.out.println(f1.join());//请重试!
    }

}
  • 根据控制台,我们可以看出执行流程是这样,supplyAsync->whenComplete->exceptionally,,可以看出并没有进入thenApply执行,可见,thenApply只有当正常返回时才会去执行,而whenComplete不管是否正常执行都会进入。还要注意一点,whenComplete是没有返回值的。

  • 上面代码我们先调用whenComplete再调用exceptionally。如果我们先调用exceptionally再调用whenComplete会发生什么呢。结果是输出“返回结果是:请重试!请重试!”


public class MyWhenComplete {
    public static void main(String[] args) {
        CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "结果是:" + 100 / 0)
                .thenApply(s -> "正确的结果是:" + s)
                .exceptionally(e -> {
                            System.out.println(e.getMessage());
                            return "请重试!";
                        }
                )
                .whenComplete((s, e) -> {
                    if (Objects.isNull(e)) {
                        System.out.println("返回结果是:"+s);//返回结果是:请重试!
                    } else {
                        System.out.println(e.getMessage());//java.lang.ArithmeticException: / by zero
                    }
                });
        System.out.println(f1.join());//请重试!
    }
}

handle(…)

  • 功能::当CompletableFuture的计算结果完成,或者抛出异常的时候,可以通过handle方法对结果进行处理。
  • handle和whenComplete的区别
     1.都是对结果进行处理,handle有返回值,whenComplete没有返回值
     2.由于1的存在,使得handle多了一个特性,可在handle里实现exceptionally的功能
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,Executor executor);
  • 场景:用handle实现whenComplete+exceptionally的组合功能。
public class MyHandle {
   public static void main(String[] args) {
       CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "结果是:" + 100 / 0)
               .thenApply(s -> "正确的结果是:" + s)
               .handle((s,e)->{
                   if(Objects.isNull(e)){
                       System.out.println(s);//未执行
                   }else{
                       System.out.println(e.getMessage());//java.lang.ArithmeticException: / by zero
                   }
                   return "结果是:" + (s == null ? "请重试" : s);
               })
               .exceptionally(e-> e.getMessage());//未执行
       System.out.println(f1.join());//输出”结果是:请重试“
   }
}

allOf(…) anyOf(…)

  • 功能: 
    1.allOf:当所有的CompletableFuture都执行完后执行计算
    2.anyOf:最快的那个CompletableFuture执行完之后执行计算
public static CompletableFuture<Void>  allOf(CompletableFuture<?>... cfs)
public static CompletableFuture<Object>  anyOf(CompletableFuture<?>... cfs)
  • 场景:根据货物id分别查询订单,支付,物流,积分。
 public static void main(String[] args) {
       ExecutorService executorService = Executors.newFixedThreadPool(4);

       CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
           try {
               Thread.sleep(1000 + RandomUtils.nextInt(1000));
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           return "订单详情";
       },executorService)
               .whenComplete((s,e)-> System.out.println(s));

       CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> {
           return "支付信息";
       },executorService)
               .whenComplete((s,e)-> System.out.println(s));;

       CompletableFuture<String> futureC = CompletableFuture.supplyAsync(() -> {

           return "物流信息";
       },executorService)
               .whenComplete((s,e)-> System.out.println(s));;

       CompletableFuture<String> futureD = CompletableFuture.supplyAsync(() -> {
           return "积分信息";
       },executorService)
               .whenComplete((s,e)-> System.out.println(s));;

       CompletableFuture.allOf(futureA, futureB, futureC, futureD);
       /*输出:
       “支付信息
       物流信息
       积分信息
       订单详情”*/
       executorService.shutdown();
   }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值