重要的參考資料:
從CompletableFuture到異步編程設計
聊聊java高併發系統之異步非阻塞
商品詳情頁系統的Servlet3異步化實踐
下面一個簡單的例子說明CompletableFuture異步編排的使用:
public class CompletableFuturePerformance {
// 使用自定義的線程池(如果使用默認ForkJoinPool,開啓的線程數爲parallelism(cpu核心數-1),執行時間會略有延長)
// 程序空閒60s後會自動停止,因爲keepAliveTime = 60
static ExecutorService executor = Executors.newCachedThreadPool();
public static void main(String[] args) {
long start = System.currentTimeMillis();
ShopPrice parallelStreamTest = new ParallelStreamTest();
ShopPrice completableFutureTest = new CompletableFutureTest();
System.out.println("ParallelStream -> " + parallelStreamTest.findPrice("java8實戰"));
long duration = System.currentTimeMillis() - start;
System.out.println("findPrice總消耗時間:" + duration + "毫秒"); // 並行流需2s,因爲我的電腦是4核,但是有5個任務
start = System.currentTimeMillis();
System.out.println("CompletableFuture -> " + completableFutureTest.findPrice("java8實戰"));
duration = System.currentTimeMillis() - start;
System.out.println("findPrice總消耗時間:" + duration + "毫秒");
start = System.currentTimeMillis();
System.out.println("ParallelStream -> " + parallelStreamTest.findPriceWithDiscount("java8實戰"));
duration = System.currentTimeMillis() - start;
System.out.println("findPriceWithDiscount總消耗時間:" + duration + "毫秒");
start = System.currentTimeMillis();
System.out.println("CompletableFuture -> " + completableFutureTest.findPriceWithDiscount("java8實戰"));
duration = System.currentTimeMillis() - start;
System.out.println("findPriceWithDiscount總消耗時間:" + duration + "毫秒");
start = System.currentTimeMillis();
System.out.println("CompletableFuture -> " + completableFutureTest.findPriceWithDiscountAsync("java8實戰"));
duration = System.currentTimeMillis() - start;
System.out.println("findPriceWithDiscountAsync總消耗時間:" + duration + "毫秒");
}
private interface ShopPrice {
List<String> findPrice(String product);
List<String> findPriceWithDiscount(String product);
default List<Double> findPriceWithDiscountAsync(String product) {
return null;
}
}
static List<Shop> shops = Arrays.asList(
new Shop("TAO BAO"),
new Shop("淘寶"),
new Shop("拼多多"),
new Shop("京東商城"),
new Shop("天貓商城"));
/**
* 價格解析類
*
* @author lsg
*/
public static class Quote {
private final String shopName;
private final double price;
private final Discount.Code code;
public Quote(String shopName, double price, Discount.Code code) {
this.shopName = shopName;
this.price = price;
this.code = code;
}
public String getShopName() {
return shopName;
}
public double getPrice() {
return price;
}
public Discount.Code getCode() {
return code;
}
public static Quote parse(String s){
String[] arr = s.split(":");
return new Quote(arr[0], Double.valueOf(arr[1]), Discount.Code.valueOf(arr[2]));
}
}
public static class Shop {
private String name;
private Random random = new Random();
public Shop(String name) {
this.name = name;
}
public String getName() {
return name;
}
/**
* 直接獲取價格
* @param product
* @return
*/
public double getPrice(String product) {
return calculatePrice(product);
}
/**
* 獲取價格描述信息
* @param product
* @return
*/
public String getPrice2(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);
}
//異步獲取價格
public Future<Double> getPriceAsync(String product) {
return CompletableFuture.supplyAsync(() ->
calculatePrice(product)
);
}
//模擬獲取價格的服務
private double calculatePrice(String product) {
delay();
return random.nextDouble() * product.charAt(0) + product.charAt(1);
}
}
public static class Discount {
public static double getRate() {
delay();
return 1d;
}
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.getCode());
}
public static double apply(double price, Code code){
delay();
return price * (100 - code.percentage) /100 ;
}
}
//模擬延遲1s
private static void delay() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static class ParallelStreamTest implements ShopPrice {
public List<String> findPrice(String product){
return shops.parallelStream()
.map(shop -> String.format("%s 的價格是 %.2f", shop.getName(),shop.getPrice(product)))
.collect(toList());
}
public List<String> findPriceWithDiscount(String product){
return shops.parallelStream()
.map(shop -> shop.getPrice2(product)) //獲取原始報價
.map(Quote::parse) //解析報價字符串
.map(Discount::applyDiscount) //調用折扣服務應用報價折扣
.collect(toList());
}
}
public static class CompletableFutureTest implements ShopPrice{
public List<String> findPrice(String product) {
List<CompletableFuture<String>> priceFuture = shops.stream()
.map(shop -> CompletableFuture.supplyAsync( // 使用異步的方式計算每種商品的價格
() -> shop.getName() + " 的價格是 " + shop.getPrice(product), executor))
.collect(toList());
return priceFuture.stream()
.map(CompletableFuture::join) //join 操作等待所有異步操作的結果
.collect(toList());
}
public List<String> findPriceWithDiscount(String product){
List<CompletableFuture<String>> priceFuture = shops.stream()
.map(shop -> CompletableFuture.supplyAsync( // 異步獲取價格
() -> shop.getPrice2(product),executor))
.map(future -> future.thenApply(Quote::parse)) // 獲取到價格後對價格解析
.map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync( // 另一個異步任務構造異步應用報價
() -> Discount.applyDiscount(quote),executor)))
.collect(toList());
return priceFuture.stream()
.map(CompletableFuture::join) //join 操作和get操作有相同的含義,等待所有異步操作的結果。
.collect(toList());
}
public List<Double> findPriceWithDiscountAsync(String product) {
List<CompletableFuture<Double>> priceFuture = shops.stream()
.map(shop -> CompletableFuture.supplyAsync( // 異步獲取價格
() -> shop.getPrice(product),executor))
.map(future -> future.thenCombine(CompletableFuture.supplyAsync( // 異步獲取折扣率
() -> Discount.getRate(),executor)
, (price, rate) -> price * rate)) // 將兩個異步任務的結果合併
.collect(toList());
return priceFuture.stream()
.map(CompletableFuture::join) //join 操作和get操作有相同的含義,等待所有異步操作的結果。
.collect(toList());
}
}
}