使用CompletableFuture优化你的代码执行效率 您所在的位置:网站首页 CompletableFuture异步传参 使用CompletableFuture优化你的代码执行效率

使用CompletableFuture优化你的代码执行效率

2023-10-09 16:16| 来源: 网络整理| 查看: 265

  这篇文章详细讲解java8中CompletableFuture的特性,方法以及实例.

  在java8以前,我们使用java的多线程编程,一般是通过Runnable中的run方法来完成,这种方式,有个很明显的缺点,就是,没有返回值,这时候,大家可能会去尝试使用Callable中的call方法,然后用Future返回结果,如下:

public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newSingleThreadExecutor(); Future stringFuture = executor.submit(new Callable() { @Override public String call() throws Exception { Thread.sleep(2000); return "async thread"; } }); Thread.sleep(1000); System.out.println("main thread"); System.out.println(stringFuture.get()); }

  通过观察控制台,我们发现先打印 main thread ,一秒后打印 async thread,似乎能满足我们的需求,但仔细想我们发现一个问题,当调用future的get()方法时,当前主线程是堵塞的,这好像并不是我们想看到的,另一种获取返回结果的方式是先轮询,可以调用isDone,等完成再获取,但这也不能让我们满意.

  不管怎么看,这种用法看起来并不优雅,起码从视觉上就有些丑陋,而且某些场景无法使用,比如说,

  1.很多个异步线程执行时间可能不一致,我的主线程业务不能一直等着,这时候我可能会想要只等最快的线程执行完或者最重要的那个任务执行完,亦或者我只等1秒钟,至于没返回结果的线程我就用默认值代替.

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

  java8的CompletableFuture,就在这混乱且不完美的多线程江湖中闪亮登场了.CompletableFuture让Future的功能和使用场景得到极大的完善和扩展,提供了函数式编程能力,使代码更加美观优雅,而且可以通过回调的方式计算处理结果,对异常处理也有了更好的处理手段.

  CompletableFuture源码中有四个静态方法用来执行异步任务:

创建任务 public static CompletableFuture supplyAsync(Supplier supplier){..} public static CompletableFuture supplyAsync(Supplier supplier,Executor executor){..} public static CompletableFuture runAsync(Runnable runnable){..} public static CompletableFuture runAsync(Runnable runnable, Executor executor){..}

  如果有多线程的基础知识,我们很容易看出,run开头的两个方法,用于执行没有返回值的任务,因为它的入参是Runnable对象,而supply开头的方法显然是执行有返回值的任务了,至于方法的入参,如果没有传入Executor对象将会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码.在实际使用中,一般我们使用自己创建的线程池对象来作为参数传入使用,这样速度会快些.

  执行异步任务的方式也很简单,只需要使用上述方法就可以了:

CompletableFuture future = CompletableFuture.supplyAsync(() -> {     //....执行任务     return "hello";}, executor)

  接下来看一下获取执行结果的几个方法.

V get(); V get(long timeout,Timeout unit);T getNow(T defaultValue);T join();

  上面两个方法是Future中的实现方式,get()会堵塞当前的线程,这就造成了一个问题,如果执行线程迟迟没有返回数据,get()会一直等待下去,因此,第二个get()方法可以设置等待的时间.

   getNow()方法比较有意思,表示当有了返回结果时会返回结果,如果异步线程抛了异常会返回自己设置的默认值.

接下来以一些场景的实例来介绍一下CompletableFuture中其他一些常用的方法.

thenAccept() public CompletionStage thenAccept(Consumer... cfs) public static CompletableFuture anyOf(CompletableFuture... cfs)

  allOf:当所有的CompletableFuture都执行完后执行计算

  anyOf:最快的那个CompletableFuture执行完之后执行计算

  场景二:查询一个商品详情,需要分别去查商品信息,卖家信息,库存信息,订单信息等,这些查询相互独立,在不同的服务上,假设每个查询都需要一到两秒钟,要求总体查询时间小于2秒.

public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(4); long start = System.currentTimeMillis(); CompletableFuture futureA = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000 + RandomUtils.nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } return "商品详情"; },executorService); CompletableFuture futureB = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000 + RandomUtils.nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } return "卖家信息"; },executorService); CompletableFuture futureC = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000 + RandomUtils.nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } return "库存信息"; },executorService); CompletableFuture futureD = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000 + RandomUtils.nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } return "订单信息"; },executorService); CompletableFuture allFuture = CompletableFuture.allOf(futureA, futureB, futureC, futureD); allFuture.join(); System.out.println(futureA.join() + futureB.join() + futureC.join() + futureD.join()); System.out.println("总耗时:" + (System.currentTimeMillis() - start)); }

参考资料:

  https://colobu.com/2016/02/29/Java-CompletableFuture/#Either

  https://blog.csdn.net/qq_36597450/article/details/81232051



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有