Cffu介绍

CompletableFuture

CompletableFuture简单介绍

CompletableFuture是一个强大的工具,用于处理异步编程任务。它属于Java 8引入的并发库的一部分,旨在简化异步编程的复杂性。

CompletableFuture类定义。

CompletionStage接口描述了一个异步计算的阶段。很多计算可以分成多个阶段或步骤,此时可以通过它将所有步骤组合起来,形成异步计算的流水线。

CompletableFuture的api

一.创建CompletableFuture对象
//异步执行一个有返回值的任务
CompletableFuture demoFuture1 =CompletableFuture.supplyAsync(()->{return"ss";});
//异步执行一个无返回值的任务
CompletableFuture demoFuture2 =CompletableFuture.runAsync(()->{System.out.println("ss");});
//创建一个已经完成的CompletableFuture对象
CompletableFuture demoFuture3 =CompletableFuture.completedFuture("ss");
// 创建一个未完成的CompletableFuture对象
CompletableFuture  demoFuture4 =newCompletableFuture();
二.组合CompletableFuture对象
//在CompletableFuture对象上执行一个函数,并返回一个新的带返回值的CompletableFuture对象
CompletableFuture<String> demoFuture1 = CompletableFuture.supplyAsync(()-> "ss");
CompletableFuture demoFuture2 = demoFuture1.thenApply((String s)->{    return s+"t";});

//在CompletableFuture对象上执行一个消费者,并返回一个新的CompletableFuture对象
CompletableFuture demoFuture3 = demoFuture1.thenAccept((String s)->{   System.out.println(s);});

//在CompletableFuture对象上执行一个Runnable,并返回一个新的CompletableFuture对象
CompletableFuture demoFuture4 = demoFuture1.thenRun(()->{    System.out.println("ss");});

//在CompletableFuture对象上执行一个函数,返回一个新的CompletableFuture对象 和thenApply很像,但是需要自己手动返回CompletableFuture对象
CompletableFuture<String> demoFuture5 = demoFuture1.thenCompose((String s)->{       
       System.out.println(s); 
       return new CompletableFuture();
});
//将两个CompletableFuture对象的结果组合起来,并执行一个函数,返回一个新的
CompletableFuture对象demoFuture1.thenCombine(demoFuture5,(t,s)->{    return s+t;});

//allOf(CompletableFuture... cfs):等待所有的CompletableFuture对象执行完成
CompletableFuture  allofCompletableFuture = CompletableFuture.allOf(demoFuture1,demoFuture2);

//anyOf(CompletableFuture... cfs):等待任意一个CompletableFuture对象执行完成
CompletableFuture  anyofCompletableFuture = CompletableFuture.anyOf(demoFuture1,demoFuture2);

//acceptEither(CompletionStage other, Consumer action):两个CompletableFuture对象任意一个完成后执行指定的消费者
CompletableFuture acceptEitherFuture = demoFuture1.acceptEither(demoFuture2,(s)->{    System.out.println(s);});

//applyToEither(CompletionStage other, Function fn):两个CompletableFuture对象任意一个完成后执行指定的函数,并返回一个新的CompletableFuture对象
CompletableFuture applyToEitherFuture =  demoFuture1.applyToEither(demoFuture2,(s)->{    return s;});

//runAfterBoth(CompletionStage other, Runnable action):两个CompletableFuture对象都完成后执行指定的Runnable
CompletableFuture runAfterBothFuture =  demoFuture1.runAfterBoth(demoFuture2,()->{    System.out.println("ss");});

//runAfterEither(CompletionStage other, Runnable action):两个CompletableFuture对象任意一个完成后执行指定的Runnable
CompletableFuture runAfterEitherFuture =  demoFuture1.runAfterEither(demoFuture2,()->{    System.out.println("ss");});

//thenAcceptBoth(CompletionStage other, BiConsumer action):将两个CompletableFuture对象的结果组合起来,并执行一个消费者
CompletableFuture thenAcceptBothFuture = demoFuture1.thenAcceptBoth(demoFuture2,(s,t)->{    System.out.println(s);    System.out.println(t);});
三.处理CompletableFuture对象的结果
//get():获取CompletableFuture对象的结果,如果任务还未完成,则会阻塞等待
CompletableFuture<String> demoFuture1 = CompletableFuture.supplyAsync(()-> "ss");
try {    
  demoFuture1.get();
} catch (InterruptedException e) 
{    
  e.printStackTrace();
} catch (ExecutionException e) {    
  e.printStackTrace();
}
//get(long timeout, TimeUnit unit):获取CompletableFuture对象的结果,如果任务还未完成,则会阻塞等待一段时间
try {    
   demoFuture1.get(1000, TimeUnit.MILLISECONDS);
   } catch (InterruptedException e) {    
     e.printStackTrace();
   } catch (ExecutionException e) {   
    e.printStackTrace();
   } catch (TimeoutException e) {    
    e.printStackTrace();
   }
//join():获取CompletableFuture对象的结果,如果任务还未完成,则会一直阻塞,和get的区别在于不抛出异常,get方法需要手动捕获
demoFuture1.join();
//cancel(boolean mayInterruptIfRunning):尝试取消CompletableFuture对象的执行
demoFuture1.cancel(true);
//isCancelled():判断CompletableFuture对象是否被取消
demoFuture1.isCancelled();
//isCompletedExceptionally():判断CompletableFuture对象是否执行异常
demoFuture1.isCompletedExceptionally();
//isDone():判断CompletableFuture对象是否执行完成
demoFuture1.isDone();
四.结果和异常处理
//exceptionally(Function fn):处理CompletableFuture对象执行过程中的异常
CompletableFuture<String> demoFuture1 = CompletableFuture.supplyAsync(()-> "ss");

demoFuture1.exceptionally((Throwable e)->{    
 System.out.println(e.getMessage());    
 return "s";
});

//handle(BiFunction fn):处理CompletableFuture对象执行过程中的异常,同时处理正常结果
demoFuture1.handle((t,e)->{    
 System.out.println(t.toUpperCase());   
 System.out.println(e.getMessage());    
 return "s";
});
五.指定业务线程池
private static final Executor executor = TtlExecutors.getTtlExecutor(new ThreadPoolExecutor(1,1,3000, TimeUnit.MILLISECONDS,new ArrayBlockingQueue(100)));
//使用指定的线程池执行异步任务,不指定的话默认走ForkJoinPool线程池
//默认ForkJoinPool线程池,ForkJoinPool里面的线程都是daemon线程,非daemon线程都结束后,虚拟机也就退出了。
//如果需要执行较长时间或执行内容比较重要不希望被中断而导致数据不一致的话,那就自己传一个Executor吧

CompletableFuture<String> demoFuture1 = CompletableFuture.supplyAsync(()-> "ss",executor);
CompletableFuture demoFuture2 = CompletableFuture.runAsync(()->{    System.out.println("s");},executor);

CompletableFuture优点

1. 异步编程:CompletableFuture提供了一种简单且强大的方式来进行异步编程,可以避免阻塞线程,提高系统的并发性能。当然获取结果的时候还是会阻塞的。

2. 链式操作:CompletableFuture支持链式调用,可以通过一系列的操作来组合多个CompletableFuture,实现复杂的异步任务。

3. 异常处理:CompletableFuture提供了异常处理的机制,可以通过exceptionally()、handle()等方法来处理异常,保证程序的健壮性。

4.提高单机吞吐量:提高单机系统的响应能力和并发性能,但要注意gc的瓶颈。

CompletableFuture缺点

1. 学习成本高:CompletableFuture的使用相对复杂,需要掌握一些异步编程的概念和技巧,对于初学者来说学习成本较高。

2. 可读性差:由于CompletableFuture支持链式调用,代码可能会变得冗长和难以理解,特别是当有多个异步任务需要组合时。

3. 线程资源占用,并发问题排查难度大:CompletableFuture在执行异步任务时会占用一定的线程资源,如果不合理地使用CompletableFuture,可能会导致线程资源的浪费,同时导致问题难以排查。

CompletableFuture在项目中的使用和扩展需要

简单的异步执行,比如在主流程执行后,异步执行同步信息。

接口逻辑中,对不同中台下发的信息的统一并行处理,和最终阻塞获取结果。

图片来自:CompletableFuture 详解 | JavaGuide

如上在项目中实际应用CompletableFuture的时候,主要使用一些基本的api,异步操作的阻塞获取、CompletableFuture的allof操作等。那么面临一些需要复杂编排的业务场景该怎么处理呢?

可以看出如果是一个比较复杂的场景,整体链式会特别长,尤其是多个超过两个以上的function执行的时候,不同并发的策略同时也得自己实现,代码异常复杂和难读。需要更多的并发策略来支撑。

Cffu的简单介绍

仓库地址: GitHub - foldright/cffu: 🦝 Java CompletableFuture-Fu(CF-Fu, pronounced "Shifu"), a tiny sidekick library to make CompletableFuture usage more convenient, more efficient and safer in your application. 😋🚀🦺

cffu是一个小小的CompletableFuture(CF)辅助增强库,提升CF使用体验并减少误用,在业务中更方便高效安全地使用CF

Cffu的api介绍

一.有返回值的allof方法

public class AllResultsOfDemo {
  public static final Executor myBizExecutor = Executors.newCachedThreadPool();
  public static final CffuFactory cffuFactory = CffuFactory.builder(myBizExecutor).build();

  public static void main(String[] args) throws Exception {
    //
    // CffuFactory#allResultsOf
    //
    Cffu<Integer> cffu1 = cffuFactory.completedFuture(21);
    Cffu<Integer> cffu2 = cffuFactory.completedFuture(42);

    Cffu<Void> all = cffuFactory.allOf(cffu1, cffu2);
    // Result type is Void!
    //
    // the result can be got by input argument `cf1.get()`, but it's cumbersome.
    // so we can see a lot of util methods to enhance `allOf` with result in our project.

    Cffu<List<Integer>> allResults = cffuFactory.allResultsOf(cffu1, cffu2);
    System.out.println(allResults.get());

    //
    // or CompletableFutureUtils#allResultsOf
    //
    CompletableFuture<Integer> cf1 = CompletableFuture.completedFuture(21);
    CompletableFuture<Integer> cf2 = CompletableFuture.completedFuture(42);

    CompletableFuture<Void> all2 = CompletableFuture.allOf(cf1, cf2);
    // Result type is Void!

    CompletableFuture<List<Integer>> allResults2 = allResultsOf(cf1, cf2);
    System.out.println(allResults2.get());
  }
}

二.不同类型结果集的返回

public class AllTupleOfDemo {
  public static final Executor myBizExecutor = Executors.newCachedThreadPool();
  public static final CffuFactory cffuFactory = CffuFactory.builder(myBizExecutor).build();

  public static void main(String[] args) throws Exception {
    //
    // allTupleFailFastOf / allTupleOf
    //
    Cffu<String> cffu1 = cffuFactory.completedFuture("21");
    Cffu<Integer> cffu2 = cffuFactory.completedFuture(42);

    Cffu<Tuple2<String, Integer>> allTuple = cffuFactory.allTupleFailFastOf(cffu1, cffu2);
    System.out.println(allTuple.get());

    //
    // or CompletableFutureUtils.allTupleFailFastOf / allTupleOf
    //
    CompletableFuture<String> cf1 = CompletableFuture.completedFuture("21");
    CompletableFuture<Integer> cf2 = CompletableFuture.completedFuture(42);

    CompletableFuture<Tuple2<String, Integer>> allTuple2 = allTupleFailFastOf(cf1, cf2);
    System.out.println(allTuple2.get());
  }
}

三.高效的并发策略

  • CompletableFutureallOf方法会等待所有输入CF运行完成;即使有CF失败了也要等待后续CF都运行完成,再返回一个失败的CF
    • 对于业务逻辑来说,这样失败且继续等待策略,减慢了业务响应性;会希望如果有输入CF失败了,则快速失败不再做于事无补的等待
    • cffu提供了相应的allResultsFailFastOf等方法
    • allOf/allResultsFailFastOf两者都是,只有当所有的输入CF都成功时,才返回成功结果
  • CompletableFutureanyOf方法返回首个完成的CF(不会等待后续没有完成的CF,赛马模式);即使首个完成的CF是失败的,也会返回这个失败的CF结果。
    • 对于业务逻辑来说,会希望赛马模式返回首个成功的CF结果,而不是首个完成但失败的CF
    • cffu提供了相应的anySuccessOf等方法
    • anySuccessOf只有当所有的输入CF都失败时,才返回失败结果
  • 返回指定时间内多个CF中成功的结果,对于失败或超时的CF返回指定的缺省值
    • 业务最终一致性时,能返回就尽量返回有的;对于没有及时返回还在运行中处理的CF,结果会写到分布式缓存中避免重复计算,下次业务请求就有了
    • 这是个常见业务使用模式,cffu提供了相应的mostSuccessResultsOf等方法

关于多个CF的并发执行策略,可以看看JavaScript规范Promise Concurrency;在JavaScript中,Promise即对应CompletableFuture

JavaScript Promise提供了4个并发执行方法:

  • Promise.all():等待所有Promise运行成功,只要有一个失败就立即返回失败(对应cffuallResultsFailFastOf方法)
  • Promise.allSettled():等待所有Promise运行完成,不管成功失败(对应cffuallResultsOf方法)
  • Promise.any():赛马模式,立即返回首个成功的Promise(对应cffuanySuccessOf方法)
  • Promise.race():赛马模式,立即返回首个完成的Promise(对应cffuanyOf方法)

public class ConcurrencyStrategyDemo {
  public static final Executor myBizExecutor = Executors.newCachedThreadPool();
  public static final CffuFactory cffuFactory = CffuFactory.builder(myBizExecutor).build();

  public static void main(String[] args) throws Exception {
    
    // CffuFactory#allResultsFailFastOf
    // CffuFactory#anySuccessOf
    // CffuFactory#mostSuccessResultsOf
    
    final Cffu<Integer> successAfterLongTime = cffuFactory.supplyAsync(() -> {
      sleep(3000); // sleep LONG time
      return 42;
    });
    final Cffu<Integer> failed = cffuFactory.failedFuture(new RuntimeException("Bang!"));

    Cffu<List<Integer>> failFast = cffuFactory.allResultsFailFastOf(successAfterLongTime, failed);
    // fail fast without waiting successAfterLongTime
    System.out.println(failFast.exceptionNow());

    Cffu<Integer> anySuccess = cffuFactory.anySuccessOf(successAfterLongTime, failed);
    System.out.println(anySuccess.get());

    Cffu<List<Integer>> mostSuccess = cffuFactory.mostSuccessResultsOf(
        0, 100, TimeUnit.MILLISECONDS, successAfterLongTime, failed);
    System.out.println(mostSuccess.get());

    
    // or CompletableFutureUtils#allResultsFailFastOf
    //    CompletableFutureUtils#anySuccessOf
    //    CompletableFutureUtils#mostSuccessResultsOf
    
    final CompletableFuture<Integer> successAfterLongTimeCf = CompletableFuture.supplyAsync(() -> {
      sleep(3000); // sleep LONG time
      return 42;
    });
    final CompletableFuture<Integer> failedCf = failedFuture(new RuntimeException("Bang!"));

    CompletableFuture<List<Integer>> failFast2 = allResultsFailFastOf(successAfterLongTimeCf, failedCf);
    // fail fast without waiting successAfterLongTime
    System.out.println(exceptionNow(failFast2));

    CompletableFuture<Integer> anySuccess2 = anySuccessOf(successAfterLongTimeCf, failedCf);
    System.out.println(anySuccess2.get());

    CompletableFuture<List<Integer>> mostSuccess2 = mostSuccessResultsOf(
        0, 100, TimeUnit.MILLISECONDS, successAfterLongTime, failed);
    System.out.println(mostSuccess2.get());
  }
}

四.支持多action

public class MultipleActionsDemo {
  static void mRunAsyncDemo() {
    // wrap tasks to CompletableFuture first, AWKWARD! 😖
    CompletableFuture.allOf(
        CompletableFuture.runAsync(() -> System.out.println("task1")),
        CompletableFuture.runAsync(() -> System.out.println("task2")),
        CompletableFuture.runAsync(() -> System.out.println("task3"))
    );

    // just run multiple actions, fresh and cool 😋
    CompletableFutureUtils.mRunAsync(
        () -> System.out.println("task1"),
        () -> System.out.println("task2"),
        () -> System.out.println("task3")
    );
  }
}

五.更多支持

  • 支持处理指定异常类型的catching方法,而不是处理所有异常ThrowableCompletableFuture#exceptionally
  • 支持超时的join的方法,join(timeout, unit)方法
  • backport支持Java8,如orTimeout/completeOnTimeout
  • 支持设置缺省的业务线程池并封装携带,CffuFactory#builder(executor)方法

Cffu的部分源码解析

api实现

catching核心实现

orTimeout核心实现

超时join核心实现

allResultsOf核心实现

allTupleOf核心实现

多种高效并发策略实现

一.allResultsFailFastOf的策略实现

这里最重要的理解必须要么返回任何一个失败的,要么没有失败的等到所有成功的完成。

anyof(anyOf(failedOrBeIncomplete), allOf(successOrBeIncomplete));

二.anySuccessOf的策略实现

这里最重要的理解必须要么返回任何一个成功的,要么没有成功的等到所有都失败。

anyof(anyOf(successOrBeIncomplete), allOf(failedOrBeIncomplete));

三.mostSuccessResultsOf的策略实现

这里最重要的理解是在规定的未超时时间内完成所有cf的时候,去获取每个cf的结果,能获取多少就获取多少。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值