Java中CompletableFuture的作用是什么

这篇文章给大家介绍Java中CompletableFuture的作用是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

创新互联公司专注于企业全网营销推广、网站重做改版、中山网站定制设计、自适应品牌网站建设、HTML5商城开发、集团公司官网建设、外贸营销网站建设、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为中山等各大城市提供网站开发制作服务。

创建CompletableFuture对象。

以下四个静态方法用来为一段异步执行的代码创建CompletableFuture对象:

public static CompletableFuture              runAsync(Runnable runnable)  public static CompletableFuture              runAsync(Runnable runnable, Executor executor)  public static  CompletableFuture supplyAsync(Supplier supplier)  public static  CompletableFuture supplyAsync(Supplier supplier, Executor executor)

runAsync方法也好理解,它以Runnable函数式接口类型为参数,所以CompletableFuture的计算结果为空。以Async结尾会使用其它的线程去执行,没有指定Executor的方法会使用ForkJoinPool.commonPool()作为它的线程池执行异步代码。

supplyAsync方法以Supplier函数式接口类型为参数,CompletableFuture的计算结果类型为U。

因为方法的参数类型都是函数式接口,所以可以使用lambda表达式实现异步任务,比如:

CompletableFuture future = CompletableFuture.supplyAsync(() -> {      //长时间的计算任务      return "hello world";  });

计算结果完成时的处理

当CompletableFuture的计算结果完成,或者抛出异常的时候,我们可以执行特定的Action。主要是下面的方法:

public CompletableFuture  whenComplete(BiConsumer action)  public CompletableFuture  whenCompleteAsync(BiConsumer action)  public CompletableFuture  whenCompleteAsync(BiConsumer action, Executor executor)  public CompletableFuture  exceptionally(Function fn)

可以看到Action的类型是BiConsumer,它可以处理正常的计算结果,或者异常情况。

注意这几个方法都会返回CompletableFuture,当Action执行完毕后它的结果返回原始的CompletableFuture的计算结果或者返回异常。

public class Main {      private static Random rand = new Random();      private static long t = System.currentTimeMillis();      static int getMoreData() {          System.out.println("begin to start compute");          try {              Thread.sleep(10000);          } catch (InterruptedException e) {              throw new RuntimeException(e);          }          System.out.println("end to start compute. passed " + (System.currentTimeMillis() - t)/1000 + " seconds");          return rand.nextInt(1000);      }      public static void main(String[] args) throws Exception {          CompletableFuture future = CompletableFuture.supplyAsync(Main::getMoreData);          Future f = future.whenComplete((v, e) -> {              System.out.println(v);              System.out.println(e);          });          System.out.println(f.get());          System.in.read();      }  }

exceptionally方法返回一个新的CompletableFuture,当原始的CompletableFuture抛出异常的时候,就会触发这个CompletableFuture的计算,调用function计算值,否则如果原始的CompletableFuture正常计算完后,这个新的CompletableFuture也计算完成,它的值和原始的CompletableFuture的计算的值相同。也就是这个exceptionally方法用来处理异常的情况。

结果转换

由于回调风格的实现,我们不必因为等待一个计算完成而阻塞着调用线程,而是告诉CompletableFuture当计算完成的时候请执行某个function。而且我们还可以将这些操作串联起来,或者将CompletableFuture组合起来。

public  CompletableFuture  thenApply(Function fn)  public  CompletableFuture  thenApplyAsync(Function fn)  public  CompletableFuture  thenApplyAsync(Function fn, Executor executor)

这一组函数的功能是当原来的CompletableFuture计算完后,将结果传递给函数fn,将fn的结果作为新的CompletableFuture计算结果。因此它的功能相当于将CompletableFuture转换成CompletableFuture

使用例子如下:

CompletableFuture future = CompletableFuture.supplyAsync(() -> {      return 100;  });  CompletableFuture f =  future.thenApplyAsync(i -> i * 10).thenApply(i -> i.toString());  System.out.println(f.get()); //"1000"

需要注意的是,这些转换并不是马上执行的,也不会阻塞,而是在前一个stage完成后继续执行。

下面一组方法虽然也返回CompletableFuture对象,但是对象的值和原来的CompletableFuture计算的值不同。当原先的CompletableFuture的值计算完成或者抛出异常的时候,会触发这个CompletableFuture对象的计算,结果由BiFunction参数计算而得。因此这组方法兼有whenComplete和转换的两个功能。

public  CompletableFuture     handle(BiFunction fn)  public  CompletableFuture     handleAsync(BiFunction fn)  public  CompletableFuture     handleAsync(BiFunction fn, Executor executor)

它们与thenApply* 方法的区别在于handle*方法会处理正常计算值和异常,因此它可以屏蔽异常,避免异常继续抛出。而thenApply*方法只是用来处理正常值,因此一旦有异常就会抛出。

纯消费结果

上面的方法是当计算完成的时候,会生成新的计算结果(thenApply, handle),或者返回同样的计算结果(whenComplete,CompletableFuture)。CompletableFuture提供了一种处理结果的方法,只对结果执行Action,而不返回新的计算值,因此计算值为Void:

public CompletableFuture  thenAccept(Consumer action)  public CompletableFuture  thenAcceptAsync(Consumer action)  public CompletableFuture  thenAcceptAsync(Consumer action, Executor executor)

看它的参数类型也就明白了,它们是消费型函数式接口Consumer,这个接口只有输入,没有返回值。

CompletableFuture future = CompletableFuture.supplyAsync(() -> {      return 100;  });  CompletableFuture f =  future.thenAccept(System.out::println);  System.out.println(f.get());  public  CompletableFuture                thenAcceptBoth(CompletionStage other, BiConsumer action)  public  CompletableFuture                thenAcceptBothAsync(CompletionStage other, BiConsumer action)  public  CompletableFuture                thenAcceptBothAsync(CompletionStage other, BiConsumer action, Executor executor)  public CompletableFuture  runAfterBoth(CompletionStage other,  Runnable action)

thenAcceptBoth以及相关方法提供了类似的功能,当两个CompletionStage都正常完成计算的时候,就会执行提供的action,它用来组合另外一个异步的结果。

runAfterBoth是当两个CompletionStage都正常完成计算的时候,执行一个Runnable,这个Runnable并不使用计算的结果。

例子如下:

CompletableFuture future = CompletableFuture.supplyAsync(() -> {      return 100;  });  CompletableFuture f =  future.thenAcceptBoth(CompletableFuture.completedFuture(10), (x, y) -> System.out.println(x * y));  System.out.println(f.get());

更彻底地,下面一组方法当计算完成的时候会执行一个Runnable,与thenAccept不同,Runnable并不使用CompletableFuture计算的结果。

public CompletableFuture  thenRun(Runnable action)  public CompletableFuture  thenRunAsync(Runnable action)  public CompletableFuture  thenRunAsync(Runnable action,  Executor executor)

因此先前的CompletableFuture计算的结果被忽略了,这个方法返回CompletableFuture类型的对象。

CompletableFuture future = CompletableFuture.supplyAsync(() -> {      return 100;  });  CompletableFuture f =  future.thenRun(() -> System.out.println("finished"));  System.out.println(f.get());

因此,你可以根据方法的参数的类型来加速你的记忆。Runnable类型的参数会忽略计算的结果,Consumer是纯消费计算结果,BiConsumer会组合另外一个CompletionStage纯消费,Function会对计算结果做转换,BiFunction会组合另外一个CompletionStage的计算结果做转换。

组合

有时,你需要在一个future结构运行某个函数,但是这个函数也是返回某种future,也就是说是两个future彼此依赖串联在一起,它类似于flatMap。

public  CompletableFuture  thenCompose(Function> fn)  public  CompletableFuture  thenComposeAsync(Function> fn)  public  CompletableFuture  thenComposeAsync(Function> fn,  Executor executor)

这一组方法接受一个Function作为参数,这个Function的输入是当前的CompletableFuture的计算值,返回结果将是一个新的CompletableFuture,这个新的CompletableFuture会组合原来的CompletableFuture和函数返回的CompletableFuture。因此它的功能类似:

A +--> B +---> C

记住,thenCompose返回的对象并不一是函数fn返回的对象,如果原来的CompletableFuture还没有计算出来,它就会生成一个新的组合后的CompletableFuture。

例子:

CompletableFuture future = CompletableFuture.supplyAsync(() -> {      return 100;  });  CompletableFuture f =  future.thenCompose( i -> {      return CompletableFuture.supplyAsync(() -> {          return (i * 10) + "";      });  });  System.out.println(f.get()); //1000

而下面的一组方法thenCombine用来复合另外一个CompletionStage的结果。它的功能类似:

A +

|

+------> C

+------^

B +

两个CompletionStage是并行执行的,它们之间并没有先后依赖顺序,other并不会等待先前的CompletableFuture执行完毕后再执行。

public  CompletableFuture thenCombine(CompletionStage other, BiFunction fn)  public  CompletableFuture thenCombineAsync(CompletionStage other, BiFunction fn)  public  CompletableFuture thenCombineAsync(CompletionStage other, BiFunction fn, Executor executor)

其实从功能上来讲,它们的功能更类似thenAcceptBoth,只不过thenAcceptBoth是纯消费,它的函数参数没有返回值,而thenCombine的函数参数fn有返回值。

CompletableFuture future = CompletableFuture.supplyAsync(() -> {      return 100;  });  CompletableFuture future2 = CompletableFuture.supplyAsync(() -> {      return "abc";  });  CompletableFuture f =  future.thenCombine(future2, (x,y) -> y + "-" + x);  System.out.println(f.get()); //abc-100

Either

thenAcceptBoth和runAfterBoth是当两个CompletableFuture都计算完成,而我们下面要了解的方法是当任意一个CompletableFuture计算完成的时候就会执行。

public CompletableFuture        acceptEither(CompletionStage other, Consumer action)  public CompletableFuture        acceptEitherAsync(CompletionStage other, Consumer action)  public CompletableFuture        acceptEitherAsync(CompletionStage other, Consumer action, Executor executor)  public  CompletableFuture     applyToEither(CompletionStage other, Function fn)  public  CompletableFuture     applyToEitherAsync(CompletionStage other, Function fn)  public  CompletableFuture     applyToEitherAsync(CompletionStage other, Function fn, Executor executor)

acceptEither方法是当任意一个CompletionStage完成的时候,action这个消费者就会被执行。这个方法返回CompletableFuture

applyToEither方法是当任意一个CompletionStage完成的时候,fn会被执行,它的返回值会当作新的CompletableFuture的计算结果。

下面这个例子有时会输出100,有时候会输出200,哪个Future先完成就会根据它的结果计算。

Random rand = new Random();  CompletableFuture future = CompletableFuture.supplyAsync(() -> {      try {          Thread.sleep(10000 + rand.nextInt(1000));      } catch (InterruptedException e) {          e.printStackTrace();      }      return 100;  });  CompletableFuture future2 = CompletableFuture.supplyAsync(() -> {      try {          Thread.sleep(10000 + rand.nextInt(1000));      } catch (InterruptedException e) {          e.printStackTrace();      }      return 200;  });  CompletableFuture f =  future.applyToEither(future2,i -> i.toString());

辅助方法 allOf 和 anyOf

前面我们已经介绍了几个静态方法:completedFuture、runAsync、supplyAsync,下面介绍的这两个方法用来组合多个CompletableFuture。

public static CompletableFuture  allOf(CompletableFuture... cfs)  public static CompletableFuture  anyOf(CompletableFuture... cfs)

allOf方法是当所有的CompletableFuture都执行完后执行计算。

anyOf方法是当任意一个CompletableFuture执行完后就会执行计算,计算的结果相同。

下面的代码运行结果有时是100,有时是"abc"。但是anyOf和applyToEither不同。anyOf接受任意多的CompletableFuture,但是applyToEither只是判断两个CompletableFuture。anyOf返回值的计算结果是参数中其中一个CompletableFuture的计算结果,applyToEither返回值的计算结果却是要经过fn处理的。当然还有静态方法的区别,线程池的选择等。

Random rand = new Random();  CompletableFuture future1 = CompletableFuture.supplyAsync(() -> {      try {          Thread.sleep(10000 + rand.nextInt(1000));      } catch (InterruptedException e) {          e.printStackTrace();      }      return 100;  });  CompletableFuture future2 = CompletableFuture.supplyAsync(() -> {      try {          Thread.sleep(10000 + rand.nextInt(1000));      } catch (InterruptedException e) {          e.printStackTrace();      }      return "abc";  });  //CompletableFuture f =  CompletableFuture.allOf(future1,future2);  CompletableFuture f =  CompletableFuture.anyOf(future1,future2);  System.out.println(f.get());

更进一步

Guava的Future类,它的Futures辅助类提供了很多便利方法,用来处理多个Future,而不像Java的CompletableFuture,只提供了allOf、anyOf两个方法。 比如有这样一个需求,将多个CompletableFuture组合成一个CompletableFuture,这个组合后的CompletableFuture的计算结果是个List,它包含前面所有的CompletableFuture的计算结果,guava的Futures.allAsList可以实现这样的功能,但是对于java CompletableFuture,我们需要一些辅助方法:

public static  CompletableFuture> sequence(List> futures) {         CompletableFuture allDoneFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));         return allDoneFuture.thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList()));     }  public static  CompletableFuture> sequence(Stream> futures) {         List> futureList = futures.filter(f -> f != null).collect(Collectors.toList());         return sequence(futureList);     }

或者Java Future转CompletableFuture:

 public static  CompletableFuture toCompletable(Future future, Executor executor) {     return CompletableFuture.supplyAsync(() -> {          try {              return future.get();          } catch (InterruptedException | ExecutionException e) {              throw new RuntimeException(e);          }      }, executor);  }

关于Java中CompletableFuture的作用是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。


当前名称:Java中CompletableFuture的作用是什么
标题来源:http://csdahua.cn/article/geedee.html
扫二维码与项目经理沟通

我们在微信上24小时期待你的声音

解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流