Java 8 CompletableFuture:组合式异步编程

一:如何使用 这个家伙写的文章通俗易懂,而且可以简单上手,强力推荐;
https://www.cnblogs.com/baidawei/p/9447737.html;

package testThread;

import entity.Discount;
import entity.Quote;
import entity.Shop;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import static java.util.stream.Collectors.toList;

/**
 * @author xueweidong
 * @date 2020/8/11 19:43
 */
public class TestDiscount {

    public static void main(String[] args) {

        List<Shop> shops = Arrays.asList(
                new Shop("one"),
                new Shop("two"),
                new Shop("three"),
                new Shop("four"));

        long start = System.nanoTime();
        /**
         * todo 一
         * 首先,我们调用getPrice远程方法将shop对象转换成了一个字符串。每个1秒
         * 然后,我们将字符串转换为Quote对象。
         * 最后,我们将Quote对象 调用 远程 Discount服务获取折扣,返回折扣价格。每个1秒
         * 顺序执行4个商品是4秒,然后又调用了Discount服务又4秒 所以是8秒。
         * 虽然我们现在把流转换为并行流 性能会很好 但是数量大于8时也很慢。相反,使用自定义CompletableFuture执行器能够更充分的利用CPU资源。
         */
        /*List<String> str = shops.stream()
                //获取 one:120.10:GOLDD 格式字符串
                .map(shop->shop.getPrice("hhhhh"))
                //转换为 Quote 对象
                .map(Quote::parse)
                //返回 Quote的shop中的折扣价格
                .map(Discount::applyDiscount)
                .collect(toList());*/


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

        /**
         * TODO 二
         * 使用的这三个map跟同步没有太大的区别,但是使用了CompletableFuture类提供的特性,在需要的地方把他们变成了异步操作。
         *
         * thenApply方法:当第一个Future运行结束,返回CompletableFuture<String>对象转换为CompleTableFuture<Quote>对象。
         *
         * thenCompose方法:将两个异步操作进行流水线,当第一个操作完成时,将其结果作为参数传递给第二个操作。
         * 换句话说,你可以创建两个CompletableFuture对象,对第一个对象调用thenCompose,并向其传递一个函数。
         *
         * 这个方法也有Async版本:thenComposeAsync,通常带后缀的版本是讲任务移交到一个新线程,不带后缀的在当前线程执行。
         * 对于这个例子我们没有加上后缀,因为对于最终结果,或者大致的时间而言都没有多少差别,少了很多线程切换的开销。
         */
        List<CompletableFuture<String>> priceFutures = shops.stream()
                //异步获取每个shop中的价格
                .map(shop -> CompletableFuture.supplyAsync(
                        () -> shop.getPrice("hhhhh")
                ))
                //Quote对象存在时,对其返回值进行转换
                .map(future -> future.thenApply(Quote::parse))
                //使用另一个异步任务构造期望的future,申请折扣
                .map(future -> future.thenCompose(quote ->
                        CompletableFuture.supplyAsync(
                                () -> Discount.applyDiscount(quote), executor)
                ))
                .collect(toList());
        //等待流中的所有Future执行完毕,提取各自的返回值
        List<String> str = priceFutures.stream().map(CompletableFuture::join).collect(toList());


        /**
         * todo 三
         * 合并两个CompletableFuture,无论是否依赖
         *
         * 与上面不同,第二个CompletableFuture无需等待第一个CompletableFuture运行结束。
         * 而是,将两个完全不相干的CompletableFuture对象整合起来,不希望等到第一个任务完全结束才开始第二个任务。
         *
         * 这种情况应该使用thenCombine方法,它接受名为BiFunction的第二个参数,这个参数定义了当两个CompletableFuture对象完成计算后,结果如何合并。
         * 同thenCompose方法一样,thenCombine方法也提供了一个Async的版本。
         * 使用thenCombineAsync会导致BiFunction中定义的合并操作被提交到线程池中,由另一个任务以异步的方式执行。
         *
         * 回到这个例子,比如说我们现在需要第三个CompletableFuture来获取汇率,展示美元。当前两个CompletableFuture计算出结果,并由BiFunction方法完全合并后,由它来最终将诶书这一任务:
         *
         * 这里 第一个参数price 是 getPrice的返回值 double ,
         * 第二个参数 rate 是第二个工厂方法返回的0.66 偷了个懒, 最后是他们的结果进行乘法操作 返回最终结果。
         */
//        Future<Double> futurePriceUSD = CompletableFuture.supplyAsync(()->shops.get(0).getPrice("gg"))
//                .thenCombine(
//                        CompletableFuture.supplyAsync(
//                                //远程服务获取 汇率
//                                ()-> 0.66
//                        ),(price,rate) -> price * rate
//                );

        /**
         * todo 四
         * 响应CompletableFuture的completion事件
         *
         * 在本章中,所有的延迟例子都是延迟1秒钟,但是在现实世界中,有时可能更糟。到目前为止,你所实现的方法必须等待所有的商品返回时才能现实商品的价格。
         * 而你希望的效果是,只要有商品返回商品价格就在第一时间显示出来,不用等待那些还没有返回的商品。
         */
        CompletableFuture[] futures = shops.stream()
                .map(shop -> CompletableFuture.supplyAsync(
                        () -> shop.getPrice("hhhhh")
                ))
                .map(future -> future.thenApply(Quote::parse))
                .map(future -> future.thenCompose(quote ->
                        CompletableFuture.supplyAsync(
                                () -> Discount.applyDiscount(quote), executor)
                ))
                //在每个CompletableFuture上注册一个操作,该操作会在CompletableFuture完成后使用它的返回值。
                //使用thenAccept将结果输出,它的参数就是 CompletableFuture的返回值。
                .map(f -> f.thenAccept(System.out::println))
                //你可以把构成的Stream的所有CompletableFuture<void>对象放到一个数组中,等待所有的任务执行完成
                .toArray(CompletableFuture[]::new);

        //allOf方法接受一个CompletableFuture构成的数组,数组中所有的COmpletableFuture对象执行完成后,
        //它返回一个COmpletableFuture<Void>对象。所以你需要哦等待最初Stream中的所有CompletableFuture对象执行完毕,
        //对allOf方法返回的CompletableFuture执行join操作
        CompletableFuture.allOf(futures).join();

        long end = System.nanoTime();
        System.out.println(str);
        System.out.println((end - start) / 1000000);
    }
}

二:原理解析 深入学习的话可以看看.比较生涩
https://www.jianshu.com/p/b3c4dd85901e;
https://blog.csdn.net/anlian523/article/details/108023786?utm_medium=distribute.pc_feed.none-task-blog-personrec_tag-6.nonecase&depth_1-utm_source=distribute.pc_feed.none-task-blog-personrec_tag-6.nonecase&request_id=5f457c0b0388ae0b5643d766;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值