为了账号安全,请及时绑定邮箱和手机立即绑定

Java8新特性之:CompletableFuture

标签:
Java

一. CompletableFuture

    1.Future接口

        Future设计的初衷:对将来某个时刻会发生的结果进行建模。

        它建模了一种异步计算,返回一个执行运算结果的引用,当运算结束后,这个引用被返回给调用方。在Future中出发那些潜在耗时的操作把调用线程解放出来,让它能继续执行其他有价值的工作,不再需要等待耗时的操作完成。

        Future的优点:比更底层的Thread更易用。

        要使用Future,通常只需要将耗时的操作封装在一个Callable对象中,再将它提交给ExecutorService。

ExecutorService executor = Executors.newCachedThreadPool();Future<Double> future = executor.submit(new Callable<Double>() { //向ExecutorService提交一个Callable对象    @Override    public Double call() throws Exception {        return doSomeLongComputation(); //以异步方式在新的线程中执行耗时的操作    }    });doSomethingElse(); //异步操作进行的同时,你可以做其他的事情try {    //获取异步操作的结果,如果最终被阻塞,无法得到结果,那么在最多等待1秒钟之后退出    Double result = future.get(1, TimeUnit.SECONDS);} catch (ExecutionException ee) {    //加上抛出一个异常} catch (InterruptedException ie) {    //当前线程在等待过程中被中断} catch (TimeoutException te) {    //在Future对象完成之前超过已过期}


    2. 实现异步API、代码避免阻塞

        使用工厂方法supplyAsync创建CompletableFuture

public Future<Double> getPriceAsync2(String product) {    return CompletableFuture.supplyAsync(() -> calculatePrice(product));}


            supplyAsync方法接受一个生产者(Supplier)作为参数,返回一个CompletableFuture对象,该对象完成异步执行后会读取调用生产者方法的返回值。

            生产者方法会交由ForkJoinPool池中的某个执行线程(Executor)运行,但是你也可以使用supplyAsync方法的重载版本,传递第二个参数指定不同的执行线程执行生产者方法。

        join方法等待异步操作结束

            CompletableFuture类中的join方法和Future接口中的get有相同的含义,等待运行结束。并且也声明在Future接口中,唯一的不同是join方法不会抛出任何检测到的异常。因此使用它时不需要再使用try/catch语句块。

public List<String> findPrices(String product) {//使用CompletableFuture以异步方式计算每种商品的价格List<CompletableFuture<String>> priceFutures = shops.stream().map(shop -> CompletableFuture.supplyAsync(() -> shop.getName() + " price is " + shop.getPriceAsync(product))).collect(Collectors.toList());//等待所有异步操作结束return priceFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());}


        使用定制执行器

            创建一个配有线程池的执行器。

            线程数的选择:N(threads) = N(CPU) * U(CPU) * (1 + W/C)

                -- N(CPU):处理器的核的数目,可以通过Runtime.getRuntime().availableProcessors()得到;

                -- U(CPU):期望的CPU利用率(该值应该介于0和1之间);

                -- W/C:等待时间与计算时间的比率。

//创建一个线程池,线程池中线程的数目为100和商店数目二者中较小的一个值(这里100为线程池的上限)private final Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100), new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {Thread t = new Thread(r);t.setDaemon(true); //使用守护线程--这种方式不会阻止程序的关停return t;}});


            集合进行并行计算有两种方式:并行流和CompletableFutures。

                -- 计算密集型操作,并且没有I/O,推荐使用Stream接口。因为实现简单,同时效率也可能是最高的(如果所有的线程都是计算密集型的,那就没有必要创建比处理器核数更多的线程);

                -- 如果并行的工作单元还涉及等待I/O的操作(包括网络连接等待),那么使用CompletableFuture灵活性更好。这种情况下处理流的流水线中如果发生I/O等待,流的延迟特性会让我们很难判断到底什么时候触发了等待。

    3. 对多个异步任务进行流水线操作

        thenApply:将一个由字符串转换Quote的方法作为参数传递给他

        thenCompose:该方法允许你对两个异步操作进行流水线,第一个操作完成时,将其结果作为参数传递给第二个操作。

                    对第一个CompletableFuture对象调用thenCompose,并向其传递一个函数。当第一个CompletableFuture执行完毕后,他的结果将作为该函数的参数,这个函数的返回值是以第一个CompletableFuture的返回做输入计算出的第二    个CompletableFuture对象。

                    使用thenCompose减少很多线程切换开销。

        thenCombine:将两个CompletableFuture对象结果整合起来。该方法接收名为BiFunction的第二参数,这个参数定义了两个CompletableFuture对象完成计算后,如何合并。

        thenAccept:方法接收CompletableFuture执行完毕后的返回值做参数。不必等待那些还未返回的结果。


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消