Java8 CompletableFuture组合式的编程(笔记)
    * 实现异步API
public double getPrice(String product) {
    return calculatePrice(product);
}
/**
 * 同步计算商品价格的方法
 *
 * @param product 商品名称
 * @return 价格
 */
private double calculatePrice(String product) {
    delay();
    return random.nextDouble() * product.charAt(0) + product.charAt(1);
}
/**
 * 模拟计算,查询数据库等耗时
 */
public static void delay() {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
    * 将同步方法装换为异步方法
/**
 * 异步计算商品的价格.
 *
 * @param product 商品名称
 * @return 价格
 */
public Future<Double> getPriceAsync(String product) {
    CompletableFuture<Double> futurePrice = new CompletableFuture<>();
    new Thread(() -> {
        double price = calculatePrice(product);
        futurePrice.complete(price);
    }).start();
    return futurePrice;
}
使用异步API 模拟客户端
Shop shop = new Shop("BestShop");
long start = System.nanoTime();
Future<Double> futurePrice = shop.getPriceAsync("my favorite product");
long incocationTime = (System.nanoTime() - start) / 1_000_000;
System.out.println("执行时间:" + incocationTime + " msecs");
try {
    Double price = futurePrice.get();
    System.out.printf("Price is %.2f%n", price);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}
long retrievalTime = (System.nanoTime() - start) / 1_000_000;
System.out.println("retrievalTime:" + retrievalTime + " msecs");

* 错误处理
     上述代码,如果没有意外,可以正常工作,但是如果价格计算过程中生产了错误会怎样呢?非常不幸,这种情况下你会得到一个相当糟糕的结果:用于提示错误的异常会限制在视图计算商品的价格的当前线程的范围内,最终会杀死该线程,而这会导致等待get方法放回结果的客户端永久的被阻塞,
     客户端可以使用重载的get方法,它给定了一个超时时间,这是一种推荐做法!
     为了让客户端能了解商店无法提供请求商品价格的原因.我们对代码优化,!
/**
 * 异步计算商品的价格.
 *
 * @param product 商品名称
 * @return 价格
 */
public Future<Double> getPriceAsync(String product) {
    CompletableFuture<Double> futurePrice = new CompletableFuture<>();
    new Thread(() -> {
        try {
            double price = calculatePrice(product);
            futurePrice.complete(price);
        } catch (Exception e) {
            //否则就抛出异常,完成这次future操作
            futurePrice.completeExceptionally(e);
        }
    }).start();
    return futurePrice;
}
    * 使用工厂方法supplyAsync创建CompletableFuture
/**
 * 异步计算商品的价格.
 *
 * @param product 商品名称
 * @return 价格
 */
public Future<Double> getPriceAsync(String product) {
   /* CompletableFuture<Double> futurePrice = new CompletableFuture<>();
    new Thread(() -> {
        try {
            double price = calculatePrice(product);
            futurePrice.complete(price);
        } catch (Exception e) {
            //否则就抛出异常,完成这次future操作
            futurePrice.completeExceptionally(e);
        }
    }).start();
    return futurePrice;*/
    return CompletableFuture.supplyAsync(() -> calculatePrice(product));
}
    * 让代码免受阻塞之苦
案例:最佳价格查询器
private static List<Shop> shops = Arrays.asList(new Shop("BestPrice"),
        new Shop(":LetsSaveBig"),
        new Shop("MyFavoriteShop"),
        new Shop("BuyItAll"));
/**
 * 最佳价格查询器
 *
 * @param product 商品
 * @return
 */
public static List<String> findprices(String product) {
    return shops
            .stream()
            .map(shop -> String.format("%s price is %.2f", shop.getName(), shop.getPrice(product)))
            .collect(Collectors.toList());
}
验证findprices的正确性和执行性能
long start = System.nanoTime();
System.out.println(findprices("myPhones27s"));
long duration = (System.nanoTime() - start) / 1_000_000;
System.out.println("Done in " + duration+" msecs");
执行结果:

* 使用平行流对请求进行并行操作 /**
* 最佳价格查询器(并行流)
*
* @param product 商品
* @return
*/
public static List<String> parallelFindprices(String product) {
return shops
.parallelStream()
.map(shop -> String.format("%s price is %.2f", shop.getName(), shop.getPrice(product)))
.collect(Collectors.toList());
} 同样的测试代码:
相当不错,看起来这是个简单有效的主意,对4个不同商店的查询实现了并行.所有完成操作的总耗时只有1秒多一点,让我们尝试使用CompletableFuture,将findprices方法中对不同商店的同步调用替换为异步调用.
    * 使用CompletableFuture发起异步请求
/**
 * 最佳价格查询器(异步调用实现)
 * @param product 商品
 * @return
 */
public static List<String> asyncFindprices(String product) {
    //使用这种方式,你会得到一个List<CompletableFuture<String>>,列表中的每一个CompletableFuture对象在计算完成后都包含商店的String类型的名称.
    //但是,由于你用CompletableFuture实现了asyncFindprices方法要求返回一个List<String>.你需要等待所有的future执行完毕,将其包含的值抽取出来,填充到列表中才能返回
    List<CompletableFuture<String>> priceFuture = shops
            .stream()
            .map(shop -> CompletableFuture.supplyAsync(() -> String.format("%s price is %.2f", shop.getName(), shop.getPrice(product))))
            .collect(Collectors.toList());
    //为了实现这个效果,我门可以向最初的List<CompletableFuture<String>>施加第二个map操作,对list中的每一个future对象执行join操作,一个接一个地等待他们允许结束,join和get方法
    //有相同的含义,不同的在于join不会抛出任何检测到的异常
    return priceFuture
            .stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());
}
相同的测试代码:

结果让我们失望了.我们采用异步调用新版方法,要比并行流慢了一倍.
    * 寻找更好的方案
经过我增加商店数量,然后使用三种方式反复的测试,发现了一个问题,并行流和异步调用的性能不分伯仲,究其原因都一样,它们内部采用的是同样的通用线程池,默认都使用固定数目的线程,具体线程数取决于Runtime.getRuntime.availableProcessors()放回值,然而,.CompletableFuture具有一定的优势,因为它允许你对执行器进行配置,尤其是线程池的大小,让它以适合应用需求的方式进行配置,满足程序的要求,而这是并行流API无法提供的.
    * 使用定制的执行器
private static final Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100));
/**
 * 最佳价格查询器(异步调用实现,自定义执行器)
 *
 * @param product 商品
 * @return
 */
public static List<String> asyncFindpricesThread(String product) {
    List<CompletableFuture<String>> priceFuture = shops
            .stream()
            .map(shop -> CompletableFuture.supplyAsync(() -> shop.getName() + " price is " + shop.getPrice(product), executor))
            .collect(Collectors.toList());
    return priceFuture
            .stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());
}
经过测试处理5个商店 是1秒多,处理9个商店也是1秒多
并行--使用流还是CompletableFutures?
     目前为止,我们已经知道对集合进行并行计算有两种方式,要么将其转化为并行流,利用map这样的操作开展工作,要么枚举出集合中的每一个元素,创建新的线程,在CompletableFuture内对其进行操作,后者提供了更多的灵活性,你可以调整线程池大小,二者能帮助你确保整体计算机不会因为线程都在等待I/O而发生阻塞
     我们使用这些API的建议如下:
    1. 如果你进行的是计算密集型的操作,并且没有I/O,那么推荐使用Stream接口,因为实现简单,同时效率也可能是最高的
    2. 反之,如果你并行的工作单元还涉及等待I/O的操作(包括网络连接等待).那么使用CompletableFuture是灵活性更好,你可以像前面讨论的那样,依据等待/计算,或者W/C的比率设定需要使用的线程数,
Java8 CompletableFuture组合式的编程(笔记)的更多相关文章
- Java 8 (10) CompletableFuture:组合式异步编程
		随着多核处理器的出现,提升应用程序的处理速度最有效的方式就是可以编写出发挥多核能力的软件,我们已经可以通过切分大型的任务,让每个子任务并行运行,使用线程的方式,分支/合并框架(java 7) 和并行流 ... 
- Java8函数之旅 (八) -  组合式异步编程
		前言 随着多核处理器的出现,如何轻松高效的进行异步编程变得愈发重要,我们看看在java8之前,使用java语言完成异步编程有哪些方案. JAVA8之前的异步编程 继承Thead类,重写run方法 实现 ... 
- java8的版本对组合式异步编程
		讨论了Java 8中的函数式数据处理,它可以将对集合数据的多个操作以流水线的方式组合在一起.本节继续讨论Java 8的新功能,主要是一个新的类CompletableFuture,它是对65节到83节介 ... 
- Java编程的逻辑 (94) - 组合式异步编程
		本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ... 
- 从CompletableFuture到异步编程设计
		从CompletableFuture到异步编程设计,笔者就分为2部分来分享CompletableFuture异步编程设计,前半部分总结下CompletableFuture使用实践,后半部分分享下Com ... 
- 响应式编程笔记三:一个简单的HTTP服务器
		# 响应式编程笔记三:一个简单的HTTP服务器 本文我们将继续前面的学习,但将更多的注意力放在用例和编写实际能用的代码上面,而非基本的APIs学习. 我们会看到Reactive是一个有用的抽象 - 对 ... 
- C# 高效编程笔记2
		C# 高效编程笔记2 1.理解GetHashCode()的陷阱 (1)作用:作为基于散列集合定义键的散列值,如:HashSet<T>,Dictionary<K,V>容器等 (2 ... 
- C# 高效编程笔记1
		C# 高效编程笔记1 1.使用属性而不是可访问的数据成员 (1).NET Framework中的数据绑定类仅支持属性,而不支持共有数据成员 (2)属性相比数据成员更容易修改 2.用运行时常量(read ... 
- storysnail的Linux串口编程笔记
		storysnail的Linux串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据Ge ... 
随机推荐
- 创建ProcessEngine
			activiti流程引擎是通过activiti.cfg.xml文件配置的(这并不符合Spring构建流程引擎的编码风格). ProcessEngine processEngine = ProcessE ... 
- Django基本操作
			Django官网下载页面 安装(安装最新LTS版): pip3 install django==1.11.9 创建一个django项目: 下面的命令创建了一个名为"s8"的Djan ... 
- kotlin 写的一个简单 sql 查询解析器
			package com.dx.efuwu.core import org.apache.commons.lang.StringUtils import java.sql.PreparedStateme ... 
- [c] 段错误(core dump): 一个格式化输出引起的问题
			#include <stdio.h> int len = sizeof(int); printf("%s\n",len); /* 编译的时候是没问题的,运行的时候就报错 ... 
- 初始github——git的简单使用
			初学者~ 有两篇吧,一篇在github上 https://github.com/DefaultYuan/Git-Pro/wiki/Introduction 文章来源:<git的简单使用> ... 
- 7/26 CSU-ACM2018暑期训练3-递归&递推-选讲
			题目链接 把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法. Input 第一行是测试数据的数目t(0 <= ... 
- java开发3~5年工作经验面试题
			关于java基础 String,StringBuilder,StringBuffer区别是什么?底层数据结构是什么?分别是如何实现的? HashSet的底层实现是什么?它与HashMap有什么关系? ... 
- 训练指南 UVALive - 3989(稳定婚姻问题)
			ayout: post title: 训练指南 UVALive - 3989(稳定婚姻问题) author: "luowentaoaa" catalog: true mathjax ... 
- ASP.NET Core 2.2 基础知识(六) 配置(内含MySql+EF)
			先上一段代码,了解一下 .NET Core 配置数据的结构. 新建一个 控制台项目,添加一个文件 json.json ,文件内容如下: { "country": "cn& ... 
- ( 转 )  mysql复合索引、普通索引总结
			对于复合索引:Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分.例如索引是key index (a,b,c). 可以支持a | a,b| a,b,c 3种组合 ... 
 
			
		