【多线程】Future相关

参考:

https://tech.meituan.com/2022/05/12/principles-and-practices-of-completablefuture.html

https://blog.csdn.net/u014209205/article/details/80598209

全网最全的CompletableFuture使用教程_ 25分钟掌握completablefuture的用法(含用法思维导图、样例,待补充到博客中)   https://www.bilibili.com/video/BV1pk4y1u7fc/?spm_id_from=333.337.search-card.all.click  

 


一、Future接口

   Future用于表示异步计算的结果,只能通过阻塞或者轮询的方式获取结果,而且不支持设置回调方法,

   核心方法:

   

  

 所有已知实现类:CompletableFutureCountedCompleterForkJoinTaskFutureTaskRecursiveActionRecursiveTaskSwingWorker 

 All Known Subinterfaces:  Response <T>, RunnableFuture <V>, RunnableScheduledFuture <V>, ScheduledFuture <

guava的ListenableFuture

  Java 8之前若要设置回调一般会使用guava的ListenableFuture,回调的引入又会导致臭名昭著的回调地狱(下面的例子会通过ListenableFuture的使用来具体进行展示)

ExecutorService executor = Executors.newFixedThreadPool(5);
ListeningExecutorService guavaExecutor = MoreExecutors.listeningDecorator(executor);
ListenableFuture<String> future1 = guavaExecutor.submit(() -> {
    //step 1
    System.out.println("执行step 1");
    return "step1 result";
});
ListenableFuture<String> future2 = guavaExecutor.submit(() -> {
    //step 2
    System.out.println("执行step 2");
    return "step2 result";
});
ListenableFuture<List<String>> future1And2 = Futures.allAsList(future1, future2);
Futures.addCallback(future1And2, new FutureCallback<List<String>>() {
    @Override
    public void onSuccess(List<String> result) {
        System.out.println(result);
        ListenableFuture<String> future3 = guavaExecutor.submit(() -> {
            System.out.println("执行step 3");
            return "step3 result";
        });
        Futures.addCallback(future3, new FutureCallback<String>() {
            @Override
            public void onSuccess(String result) {
                System.out.println(result);
            }        
            @Override
            public void onFailure(Throwable t) {
            }
        }, guavaExecutor);
    }

    @Override
    public void onFailure(Throwable t) {
    }}, guavaExecutor);
View Code

  CompletableFuture的实现如下:更为简洁,可读性更好

ExecutorService executor = Executors.newFixedThreadPool(5);
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
    System.out.println("执行step 1");
    return "step1 result";
}, executor);

CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
    System.out.println("执行step 2");
    return "step2 result";
});
cf1.thenCombine(cf2, (result1, result2) -> {
    System.out.println(result1 + " , " + result2);
    System.out.println("执行step 3");
    return "step3 result";
}).thenAccept(result3 -> System.out.println(result3));
View Code

 


二、CompletableFuture

  

  • 可组合:可以将多个依赖操作通过不同的方式进行编排,例如CompletableFuture提供thenComposethenCombine等各种then开头的方法,这些方法就是对“可组合”特性的支持。
  • 操作融合:将数据流中使用的多个操作符以某种方式结合起来,进而降低开销(时间、内存)。
  • 延迟执行:操作不会立即执行,当收到明确指示时操作才会触发。例如Reactor只有当有订阅者订阅时,才会触发操作。
  • 回压:某些异步阶段的处理速度跟不上,直接失败会导致大量数据的丢失,对业务来说是不能接受的,这时需要反馈上游生产者降低调用量。

  

CompletableFuture实现了两个接口(如上图所示):Future、CompletionStage。

  • Future:表示异步计算的结果,
  • CompletionStage:表示异步执行过程中的一个步骤(Stage),这个步骤可能是由另外一个CompletionStage触发的,随着当前步骤的完成,也可能会触发其他一系列CompletionStage的执行。可以根据实际业务对这些步骤进行多样化的编排组合,CompletionStage接口正是定义了这样的能力,我们可通过thenAppy、thenCompose等函数式编程方法来组合编排这些步骤。

    

 关键方法:

  •  CompletableFuture的创建
ExecutorService executor = Executors.newFixedThreadPool(5);
//1、使用runAsync或supplyAsync发起异步调用
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
  return "result1";
}, executor);
//2、CompletableFuture.completedFuture()直接创建一个已完成状态的CompletableFuture CompletableFuture<String> cf2 = CompletableFuture.completedFuture("result2");
//3、先初始化一个未完成的CompletableFuture,然后通过complete()、completeExceptionally(),完成该CompletableFuture CompletableFuture<String> cf = new CompletableFuture<>(); cf.complete("success");

在使用CompletableFuture类在进行线程管理时,通常会使用四个静态方法来为一段异步执行的代码创建CompletableFuture对象,

    

  • 一元依赖:
CompletableFuture<String> cf3 = cf1.thenApply(result1 -> {
  //result1为CF1的结果
  //......
  return "result3";
});
CompletableFuture<String> cf5 = cf2.thenApply(result2 -> {
  //result2为CF2的结果
  //......
  return "result5";
});
  • 二元依赖:thenCombine:CF4同时依赖于两个CF1和CF2

     CompletableFuture<String> cf4 = cf1.thenCombine(cf2, (result1, result2) -> { //result1和result2分别为cf1和cf2的结果 return "result4"; });

  • 多元依赖:

    allOfanyOf:整个流程的结束依赖于三个步骤CF3、CF4、CF5,这种多元依赖可以通过allOfanyOf方法来实现,区别是当需要多个依赖全部完成时使用allOf,当多个依赖中的任意一个完成即可时使用anyOf 

CompletableFuture<Void> cf6 = CompletableFuture.allOf(cf3, cf4, cf5);
CompletableFuture<String> result = cf6.thenApply(v -> {
  //这里的join并不会阻塞,因为传给thenApply的函数是在CF3、CF4、CF5全部完成时,才会执行 。
  result3 = cf3.join();
  result4 = cf4.join();
  result5 = cf5.join();
  //根据result3、result4、result5组装最终result;
  return "result";
});
  •  异常处理:CompletableFuture提供了异常捕获回调exceptionally,相当于同步调用中的try\catch。使用方法如下所示:
@Autowired
private WmOrderAdditionInfoThriftService wmOrderAdditionInfoThriftService;//内部接口
public CompletableFuture<Integer> getCancelTypeAsync(long orderId) {
    CompletableFuture<WmOrderOpRemarkResult> remarkResultFuture = wmOrderAdditionInfoThriftService.findOrderCancelledRemarkByOrderIdAsync(orderId);//业务方法,内部会发起异步rpc调用
    return remarkResultFuture
      .exceptionally(err -> {//通过exceptionally 捕获异常,打印日志并返回默认值
         log.error("WmOrderRemarkService.getCancelTypeAsync Exception orderId={}", orderId, err);
         return 0;
      });
}

  

runAsync()和supplyAsync()方法的本质区别就是获取的CompletableFuture对象是否带有计算结果(类似于Runnable接口和Callable接口的区别)。

带有Executor参数的方法用于传入指定的线程池执行器来进行多线程管理;

而未带有Executor参数的方法会使用默认的ForkJoinPool.commonPool()作为它的线程池进行多线程管理。

supplyAsync使用默认的ForkJoinPool来执行

 

 

  

其他的方法定义类似

  

 

posted @ 2022-08-02 21:34  飞翔在天  阅读(157)  评论(0编辑  收藏  举报