Java多線程編程-10 J.U.C之CompletableFuture

重要的參考資料:
從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());
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章