现在我们要对商店商品进行折扣服务.每个折扣代码对应不同的折扣率,使用一个枚举变量Discount.Code来实现这一想法,具体代码如下所示.
以枚举类型定义的折扣代码
/**
* 折扣服务api
*
* @author Darcy
* Created by Administrator on 2017/3/17.
*/
public class Discount {
public enum Code {
NONE(0), SILVER(0), 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.getDiscountCode());
} private static double apply(double price, Code code) {
delay();
return price * (100 - code.percentage) / 100;
} /**
* 模拟计算,查询数据库等耗时
*/
public static void delay() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} 修改商店返回价格的格式: public String getPrice(String product) {
double price = calculatePrice(product);
Discount.Code code = Discount.Code.values()[
random.nextInt(Discount.Code.values().length)];
return String.format("%s:%.2f:%s", name, price, code);
} * 实现折扣服务 我们的商店已经能从不同的商店获得商品价格,解析结果字符串,针对每个字符串,查询折扣服务器取的折扣代码.这个流程决定了请求商品的最终折扣价格.我们将对商店返回的字符串的解析操作封装到了下面的Quote类中:
/**
* 商店返回消息实体,不可变对象模式 线程安全
* @author Darcy
* Created by Administrator on 2017/3/17.
*/
public final 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);
} public String getShopName() {
return shopName;
} public double getPrice() {
return price;
} public Discount.Code getDiscountCode() {
return discountCode;
}
} Discount服务还提供了一个applyDiscount方法,它接收一个Quote对象,返回一个字符串,表示生成该Quote的shop中的折扣价格,代码如下:
public static String applyDiscount(Quote quote) {
return quote.getShopName() + " 商品原价: " + quote.getPrice() + " 折扣后价格: " + Discount.apply(quote.getPrice(), quote.getDiscountCode());
} private static double apply(double price, Code code) {
delay();
return price * (100 - code.percentage) / 100;
} * 使用Discount服务 /**
* 商店折扣价格查询器
*
* @param product 商品
* @return
*/
public static List<String> findprices(String product) {
return shops
.stream()
.map(shop -> shop.getPrice(product))
.map(Quote::parse)
.map(Discount::applyDiscount)
.collect(Collectors.toList());
} 执行结果:
换成并行流:
/**
* 商店折扣价格查询器
*
* @param product 商品
* @return
*/
public static List<String> findprices(String product) {
return shops
.parallelStream()
.map(shop -> shop.getPrice(product))
.map(Quote::parse)
.map(Discount::applyDiscount)
.collect(Collectors.toList());
} 执行结果:
看到差距了吧

    * 构建同步和异步操作

我们再次使用ComoletableFuture提供的特性.以异步方式重新实现findPrices方法,详细代码如下:
/**
* 商店折扣价格查询器(CompletableFuture方式)
*
* @param product 商品
* @return
*/
public static List<String> findPrices(String product) {
List<CompletableFuture<String>> collect = shops
.stream()
//以异步凡是取得每个shop中指定产品的原始价格
.map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor))
//Quote对象存在时,对其返回值进行转换
.map(future -> future.thenApply(Quote::parse))
//使用另一个异步任务构建期望的Future,申请折扣 thenCompose 将多个future组合 一个一个执行
.map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor)))
.collect(Collectors.toList());
return collect
.stream()
//等待流中所有的future执行完毕,并提取各自的返回值
.map(CompletableFuture::join)
.collect(Collectors.toList());
} * 对最佳价格查询器应用的优化 上面的所有例子中都是通过响应之前添加1秒延迟的等待时间模拟方法的远程调用,毫无疑问,现实生活中,你的应用访问各个远程服务器时很可能遭遇无法预知的延迟,触发原因多种多样,从服务器的负荷到网络的延迟,有些甚至是源于远程服务如何评估你应用的商业价值,
由于这些原因,你希望购买的商品在某些原因的查询速度要比另一些商店更快,我们模拟了操作: /**
* 模拟不同的商店 延迟不一样的情况
*/
public static void randomDelay() {
int delay = 500 + random.nextInt(2000);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
} 重构findPrices方法 返回一个由Future构成的流:
/**
* 重构findPrices方法 返回一个由Future构成的流
*
* @param product 商品
* @return
*/
public static Stream<CompletableFuture<String>> findProcesStream(String product) {
return shops
.stream()
//以异步凡是取得每个shop中指定产品的原始价格
.map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor))
//Quote对象存在时,对其返回值进行转换
.map(future -> future.thenApply(Quote::parse))
//使用另一个异步任务构建期望的Future,申请折扣 thenCompose 将多个future组合 一个一个执行
.map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor)));
} 付诸实现:
long start = System.nanoTime();
CompletableFuture[] futures = findProcesStream("myPhones27s")
//Java 8的CompletableFuture通过thenAccept方法 他接收CompletableFuture执行完毕的返回值作为参数.
.map(f -> f.thenAccept(
s -> System.out.println(s + " (done in " +
((System.nanoTime() - start) / 1_000_000) + " msecs)")))
.toArray(CompletableFuture[]::new);
//allOf工厂方法接收一个由CompletableFuture构成的数组,数组中所有的CompletableFuture对象执行完毕后,它返回一个
//CompletableFuture<Void>对象,这意味着你需要等待最初Stream中所有的CompletableFuture对象执行完毕
//angOf该方法接收一个CompletableFuture对象构成的数组,返回由第一个执行完毕的CompletableFuture对象的返回值构成的CompletableFuture<Object>
CompletableFuture.allOf(futures).join();
System.out.println("All shops have now responded in " + ((System.nanoTime() - start) / 1_000_000) + " msecs"); 执行结果:
 

Java8 对多个异步任务进行流水线操作(笔记)的更多相关文章

  1. 【Java8新特性】面试官:谈谈Java8中的Stream API有哪些终止操作?

    写在前面 如果你出去面试,面试官问了你关于Java8 Stream API的一些问题,比如:Java8中创建Stream流有哪几种方式?(可以参见:<[Java8新特性]面试官问我:Java8中 ...

  2. sobel流水线操作Verilog程序

    sobel算子的verilog实现,采用了流水线操作 module sobel_computer ( clock , reset, OrigDataEn, //SobelAluEn, OrigData ...

  3. Java8系列 (七) CompletableFuture异步编程

    概述 Java8之前用 Future 处理异步请求, 当你需要获取任务结果时, 通常的做法是调用  get(long timeout, TimeUnit unit) 此方法会阻塞当前的线程, 如果任务 ...

  4. koa框架异步返回值的操作(co,koa-compose)

    最近在做demo的时候使用了koa框架,自己做了一个静态服务器,首先判断访问文件是否存在,在回调函数中设置了this.body,run之后,各种404,花了N长的时间把koa-compose和co模块 ...

  5. 事件异步(EAP)使用事件异步处理一些耗时操作

    比如需要下载一些比较大的文件,如果使用会UI卡顿,使用异步可以节省一些时间 下面是一些例子: using System; using System.Collections.Generic; using ...

  6. Oracle存储过程中异步调用的实际操作步骤

    本文标签:Oracle存储过程 我们都知道在Oracle数据库的实际应用的过程中,我们经常把相关的业务处理逻辑,放在Oracle存储过程中,客户端以通过ADO来进行相关的调用  .而有些相关的业务逻辑 ...

  7. 异步任务神器 Celery 简明笔记

    转自:http://www.jianshu.com/p/1840035cb510 异步任务 异步任务是web开发中一个很常见的方法.对于一些耗时耗资源的操作,往往从主应用中隔离,通过异步的方式执行.简 ...

  8. 获取 JavaScript 异步函数返回值的笔记

    wrong action function asyncfunc() { let ret = 100; setTimeout(() => { return ret; }, 1000) } let ...

  9. 《DirectX 9.0 3D游戏开发编程基础》 第二章 绘制流水线 读书笔记

    模型的表示 场景:物品或模型的集合 任何物品都可以用三角形网络逼近表示.我们经常用以下术语描述三角形网络:多边形(polygons).图元(primitives).网络几何单元(mesh geomet ...

随机推荐

  1. 移动开发之css3实现背景渐变效果

    前段时间由于手机项目需要实现一个背景渐变功能,  开始是想切个小背景图平铺下,  后来想到css3可以实现,如是用下面的代码就实现了. .sec_case_list li  span{ backgro ...

  2. 各种编码UNICODE、UTF-8、ANSI、ASCII、GB2312、GBK详解

    来自:http://blog.csdn.net/lvxiangan/article/details/8151670 ------------------------------------------ ...

  3. delphi.memory.分配及释放---New/Dispose, GetMem/FreeMem及其它函数的区别与相同,内存分配函数

    来自:http://www.cnblogs.com/qiusl/p/4028437.html?utm_source=tuicool&utm_medium=referral ---------- ...

  4. Django基础之视图

    Django的View(视图) 一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应. 响应可以是一张网页的HTML内容,一个重定向,一个404错误, ...

  5. Eclipse默认标签TODO,XXX,FIXME和自定义标签

    1 TODO 表示需要实现,但目前还未实现的功能 2 XXX 勉强可以工作,但是需要改进的功能 3 FIXME 代码是错误的,不能工作,需要修复 4.自定义标签 window-->prefere ...

  6. configure.ac中AC_CHECK_LIB的问题

    编译Linux程序时,使用configure.ac生成的configure程序,时常会出现AC_CHECK_LIB检查某个库失败 而相应库通常是存在的,只是依赖于其他的库,此时,需要乃至AC_CHEC ...

  7. 使用moneykey对APP进行健壮性测试

    注意:moneykey对app按钮伪随机点击,只能测试app稳定性和健壮性,无法进行常规测试 1.安装 A.jdk(不详细介绍) B.安装配置android配置环境:Android Studio 此环 ...

  8. ASP.NET Core 2.2 基础知识(十五) Swagger

    安装 Nuget 包 注册 Swagger public void ConfigureServices(IServiceCollection services) { services.AddMvc() ...

  9. 【权值分块】bzoj3224 Tyvj 1728 普通平衡树

    权值分块和权值线段树的思想一致,离散化之后可以代替平衡树的部分功能. 部分操作的时间复杂度: 插入 删除 全局排名 全局K大 前驱 后继 全局最值 按值域删除元素 O(1) O(1) O(sqrt(n ...

  10. Java高级架构师(一)第20节:X-gen生成需要的Action

    package cn.javass.themes.smvcsm.actions; import cn.javass.xgen.genconf.vo.ModuleConfModel; import cn ...