当前位置: 首页 > news >正文

对产品实现折扣服务(对多个异步任务进行流水线操作)

需求:给商品打折

1、根据产品名称返回价格
2、查询商品的折扣率
3、计算新价格

源码清单

@Data
public class DiscountShop {private final String name;private final Random random;public DiscountShop(String name) {this.name = name;random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2));}// 根据产品名称返回价格折扣信息public String getPrice(String product) {double price = calculatePrice(product);Discount.Code code = Discount.Code.values()[random.nextInt(Discount.Code.values().length)];return name + ":" + price + ":" + code;}public double calculatePrice(String product) {Util.delay();// 依据产品的名称,生成一个随机值作为价格return Util.format(random.nextDouble() * product.charAt(0) + product.charAt(1));}
}// 报价服务
@Data
public class Quote {private final String shopName;private final double price;private final Discount.Code discountCode;public Quote(String shopName, double price, Discount.Code discountCode) {this.shopName = shopName;this.price = price;this.discountCode = discountCode;}// 解析报价public static Quote parse(String s) {String[] split = s.split(":");String shopName = split[0];double price = Double.parseDouble(split[1]);Discount.Code discountCode = Discount.Code.valueOf(split[2]);return new Quote(shopName, price, discountCode);}
}// 折扣服务
ublic class Discount {public enum Code {NONE(0), SILVER(5), GOLD(10), PLATINUM(15), DIAMOND(20);private final int percentage;Code(int percentage) {this.percentage = percentage;}}// 根据折扣计算新价格public static String applyDiscount(Quote quote) {return quote.getShopName() + " price is " + Discount.apply(quote.getPrice(), quote.getDisco}// 价格打折private static double apply(double price, Code code) {Util.delay();return Util.format(price * (100 - code.percentage) / 100);}
}

实现方案

方案1:串行执行

// 串行支持
public List<String> findPricesSequential(String product) {return discountShops.stream().map(discountShop -> discountShop.getPrice(product)).map(Quote::parse).map(Discount::applyDiscount).collect(Collectors.toList());
}

方案2:并行流的方式

// 并行流的方式
// Stream底层依赖的是线程数量固定的通用线程池
public List<String> findPricesParallel(String product) {return discountShops.parallelStream().map(discountShop -> discountShop.getPrice(product)).map(Quote::parse).map(Discount::applyDiscount).collect(Collectors.toList());
}

方案3:构造同步和异步操作+返回值

private final Executor executor = Executors.newFixedThreadPool(discountShops.size(), ExecuterThreadFactoryBuilder.build());// 构造同步和异步操作
ublic List<String> findPricesFuture(String product) {List<CompletableFuture<String>> priceFutures =discountShops.stream().map(discountShop -> CompletableFuture.supplyAsync(() -> discountShop.getPrice(product), executor)).map(future -> future.thenApply(Quote::parse))// 一般情况下解析操作不涉及任何远程服务,也不会进行任何I/O操作,它几乎可以在第一时间进行,所以能够采用同步操作,不会带来太多的延迟。.map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor))).collect(Collectors.<CompletableFuture<String>>toList());return priceFutures.stream().map(CompletableFuture::join)// 等待流中的所有Future执行完毕,并提取各自的返回值.collect(Collectors.toList());

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

通常而言,名称中不带Async的方法和它的前一个任务一样,在同一个线程中运行;而名称以Async结尾的方法会将后续的任务提交到一个线程池,所以每个任务是由不同的线程处理的。

方案3:构造同步和异步操作+消费型

private final Executor executor = Executors.newFixedThreadPool(discountShops.size(), ExecuterThreadFactoryBuilder.build());// 构造同步和异步操作+消费型
public List<String> printPricesStream(String product) {long start = System.nanoTime();CompletableFuture<String>[] priceFutures = discountShops.stream().map(discountShop -> CompletableFuture.supplyAsync(() -> discountShop.getPrice(product), executor)).map(future -> future.thenApply(Quote::parse)).map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor))).peek(f -> f.thenAccept(s -> System.out.println(s + " (done in " + ((System.nanoTime() - start) / 1_000_000) + " msecs)"))).toArray(size -> new CompletableFuture[size]);System.out.println("All shops have now responded in " + ((System.nanoTime() - start) / 1_000_000) + " msecs");CompletableFuture.allOf(priceFutures).join();return null; // 仅为了统一使用stopwatch加的返回值
}

性能比较

public static void main(String[] args) {StopWatch stopWatch = new StopWatch("性能比较");execute("sequential", () -> bestPriceFinder.findPricesSequential("myPhone27S"), stopWatch);execute("parallel", () -> bestPriceFinder.findPricesParallel("myPhone27S"), stopWatch);execute("composed CompletableFuture", () -> bestPriceFinder.findPricesFuture("myPhone27S"), stopWatch);execute("composed printPricesStream", () -> bestPriceFinder.printPricesStream("myPhone27S"), stopWatch);StopWatchUtils.logStopWatch(stopWatch);
}private static void execute(String msg, Supplier<List<String>> s, StopWatch stopWatch) {stopWatch.start(msg);System.out.println(s.get());stopWatch.stop();System.out.println();
}执行结果
[BestPrice price is 110.93, LetsSaveBig price is 135.58, MyFavoriteShop price is 192.72, BuyItAll price is 184.74, ShopEasy price is 167.28][BestPrice price is 117.57, LetsSaveBig price is 174.03, MyFavoriteShop price is 173.77, BuyItAll price is 169.89, ShopEasy price is 176.43][BestPrice price is 204.78, LetsSaveBig price is 190.85, MyFavoriteShop price is 128.92, BuyItAll price is 140.31, ShopEasy price is 166.1]All shops have now responded in 7 msecs
ShopEasy price is 224.23 (done in 2034 msecs)
BuyItAll price is 111.53 (done in 2034 msecs)
MyFavoriteShop price is 119.11 (done in 2034 msecs)
BestPrice price is 127.88 (done in 2034 msecs)
LetsSaveBig price is 147.21 (done in 2034 msecs)
null性能比较 total cost time = 16226 ms
sequential                               : 10118 ms, 62.36%
parallel                                 : 2035 ms, 12.54%
composed CompletableFuture               : 2031 ms, 12.52%
composed printPricesStream               : 2040 ms, 12.57%

参考

《Java8 实战》第11章 CompletableFuture:组合式异步编程

相关文章:

  • 云汇优想:抖音矩阵系统有哪些类型?
  • 集团VPN问题排查及核心交换机(思科C9500)路由编写
  • PDF Expert for mac(专业pdf编辑器)苹果电脑
  • 数据库安全:Hadoop 未授权访问-命令执行漏洞.
  • K8s安装doris踩坑记录
  • git 简单入门
  • 阿里云OSS和腾讯云COS对象存储介绍和简单使用
  • 高并发场景下,如何设计订单库存架构,一共9个关键性问题
  • 了解防抖和节流:提升前端交互体验的实用策略
  • 【JAVA学习笔记】 68 - 网络——TCP编程、UDP编程
  • FFmpeg获取视频关键帧并保存成jpg图像
  • Centos, RockyLinux 常用软件安装汇总
  • 使用数据泵的注意事项
  • k8s自定义Endpoint实现内部pod访问外部应用
  • 307.区域和检索
  • JavaScript-如何实现克隆(clone)函数
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • 03Go 类型总结
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • Apache Spark Streaming 使用实例
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • Laravel 中的一个后期静态绑定
  • Linux CTF 逆向入门
  • Linux快速复制或删除大量小文件
  • magento 货币换算
  • miaov-React 最佳入门
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • SpiderData 2019年2月25日 DApp数据排行榜
  • Vue.js 移动端适配之 vw 解决方案
  • 全栈开发——Linux
  • 三分钟教你同步 Visual Studio Code 设置
  • 深度学习入门:10门免费线上课程推荐
  • 体验javascript之美-第五课 匿名函数自执行和闭包是一回事儿吗?
  • 用Visual Studio开发以太坊智能合约
  • 《天龙八部3D》Unity技术方案揭秘
  • Linux权限管理(week1_day5)--技术流ken
  • 测评:对于写作的人来说,Markdown是你最好的朋友 ...
  • # 安徽锐锋科技IDMS系统简介
  • #QT(智能家居界面-界面切换)
  • #单片机(TB6600驱动42步进电机)
  • (1)STL算法之遍历容器
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (day 12)JavaScript学习笔记(数组3)
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (附源码)springboot青少年公共卫生教育平台 毕业设计 643214
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • (转) ns2/nam与nam实现相关的文件
  • (转)LINQ之路
  • .bashrc在哪里,alias妙用
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .Net Core与存储过程(一)
  • .NET 材料检测系统崩溃分析