摘要:比如我們獲取價格的同時也獲取匯率遠程獲取匯率方法獲取匯率結合倆個異步操作沃爾瑪接受兩個參數對象表明第二個異步操作接口兩個異步操作的結果合并處理
【模擬情景
上一篇說到每一個shop都會提供一個價格查詢的服務,但是現在我們進行假設:
1. 所有的價格查詢是同步方式提供的 2. shop在返回價格的同時會返回一個折扣碼 3. 我們需要解析返回的字符串,并且根據折扣碼區獲取折扣后的價格 4. 折扣后的價格計算依然是同步執行的 5. 查詢價格返回的字符串格式為shopName:price:discountCode("沃爾瑪:200:15")
定義商店對象:Shop.java
public class Shop { private String name; public Shop(String name){ this.name = name; } public String getName(){ return name; } public String getPriceFormat(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); } private double calculatePrice(String product){ delay(); return random.nextDouble() * product.charAt(0) + product.charAt(1); } private Random random = new Random(); /** * 模擬耗時操作:延遲一秒 */ private static void delay(){ try { Thread.sleep(1000L); } catch (InterruptedException e) { throw new RuntimeException(e); } } }
定義折扣對象:Discount.java
public class Discount { public enum Code{ NONE(0),SILVER(5),GOLD(10),PLATINUM(15),DIAMOND(20); private final int percantage; Code(int percentage){ this.percantage = percentage; } } public static String applyDiscount(Quote quote){ return quote.getShopName() + "prices is " + Discount.apply(quote.getPrice(),quote.getDiscountCode()); } //計算折扣價格 private static Double apply(double price ,Code code){ //模擬遠程操作的延遲 delay(); return (price * (100 - code.percantage)) / 100; } private static void delay(){ try { Thread.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } } }
用于封裝解析getPriceFormat的字符串對象:Quote.java
public class Quote { private final String shopName; private final double price; private final Discount.Code discountCode; public Quote(String shopName,double price,Discount.Code code){ this.shopName = shopName; this.price = price; this.discountCode = code; } public static Quote parse(String s){ String[] split = s.split(":"); String shopName = split[0]; Double price = Double.valueOf(split[1]); Discount.Code code = Discount.Code.valueOf(split[2]); return new Quote(shopName,price,code); } public double getPrice() { return price; } public String getShopName() { return shopName; } public Discount.Code getDiscountCode() { return discountCode; } }
于是現在的任務就是:
1. 遠程查詢商品價格 2. 將獲得的字符串解析成為Quote對象 3. 根據Quote對象遠程獲取折扣后的價格
現在先看看同步的方式來執行這個操作:
public ListfindPrices2(String product){ return shops.stream() .map(shop -> shop.getPriceFormat(product)) .map(Quote::parse) .map(Discount::applyDiscount) .collect(Collectors.toList()); }
因為有兩個耗時操作,每個1秒,耗時毫無疑問20秒以上:
1. 獲取價格:使用CompletableFuture.supplyAsync()工廠方法即可,一旦運行結束每個CompletableFuture對象會包含一個shop返回的字符串,這里記住使用我們自定義的執行器。
2. 解析報價:一般情況下解析操作并不涉及到IO處理,所可以采用同步處理,所以這里我們直接使用CompletableFuture對象的thenApply()方法,表明在的帶運算結果后立刻同步處理。
3. 計算折扣價格:這是一個遠程操作,肯定是需要異步執行的,于是我們現在就有了兩次異步處理(1.獲取價格,2.計算折扣)?,F在使用級聯的方式將它們串聯起來工作。CompletableFuture提供了thenCompose方法,表明將兩個異步操作進行流水線處理。第一個異步操作的結果會成為第二個異步操作的入參。使用這樣的方式,即使Future在向不同的shop手機報價,主線程依然可以執行其他操作,比如響應UI事件。
于是我們有了如下代碼:
/** * 異步查詢 * 相比并行流的話CompletableFuture更有優勢:可以對執行器配置,設置線程池大小 */ @SuppressWarnings("all") private final Executor myExecutor = Executors.newFixedThreadPool(Math.min(shops.size(), 100), new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setDaemon(true); return t; } }); public ListfindPrices2Async(String product){ List > futurePrices = shops.stream() //首先異步獲取價格 .map(shop -> CompletableFuture.supplyAsync(() -> shop.getPriceFormat(product),myExecutor)) //將獲取的字符串解析成對象 .map(future -> future.thenApply(Quote::parse)) //使用另一個異步任務有獲取折扣價格 .map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote),myExecutor))) .collect(Collectors.toList()); //等待所有異步任務完成 return futurePrices.stream().map(CompletableFuture::join).collect(Collectors.toList());
運算結果不到3秒:
我們剛才使用thenCompose()將兩個CompletableFuture結合了起來,并且一個CompletableFuture的運算結果將作為第二個CompletableFuture的入參。但是更多的情況是兩個不相干的CompletableFuture對象相互結合,并且我們也不希望第一個任務結束之后才開始第二個任務。這時可以使用thenCombine()。
比如我們獲取價格的同時也獲取匯率:
遠程獲取匯率方法:
/** * 獲取匯率 */ public double getRate(String type){ delay(); if("$".equals(type)){ return 0.3; } if("¥".equals(type)){ return 0.7; } return 1; }
結合倆個異步操作:
@Test public void combine(){ Shop shop = new Shop("沃爾瑪"); FuturefuturePrice = CompletableFuture.supplyAsync(() -> shop.getPrice("iphoneX")) .thenCombine(CompletableFuture.supplyAsync(() -> shop.getRate("$")), (price,rate) -> price * rate); }
thenCombine()接受兩個參數:
1. CompletableFuture對象:表明第二個異步操作 2. BiFunction super T,? super U,? extends V>接口:兩個異步操作的結果合并處理
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/68237.html
摘要:相比與其他操作系統包括其他類系統有很多的優點,其中有一項就是,其上下文切換和模式切換的時間消耗非常少。因為多線程競爭鎖時會引起上下文切換。減少線程的使用。很多編程語言中都有協程。所以如何避免死鎖的產生,在我們使用并發編程時至關重要。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Java多線程學習(二)synchronized關鍵字(1) java多線程學習(二)syn...
摘要:因為多線程競爭鎖時會引起上下文切換。減少線程的使用。舉個例子如果說服務器的帶寬只有,某個資源的下載速度是,系統啟動個線程下載該資源并不會導致下載速度編程,所以在并發編程時,需要考慮這些資源的限制。 最近私下做一項目,一bug幾日未解決,總惶恐。一日頓悟,bug不可怕,怕的是項目不存在bug,與其懼怕,何不與其剛正面。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Jav...
摘要:三關鍵字能保證原子性嗎并發編程藝術這本書上說保證但是在自增操作非原子操作上不保證,多線程編程核心藝術這本書說不保證。多線程訪問關鍵字不會發生阻塞,而關鍵字可能會發生阻塞關鍵字能保證數據的可見性,但不能保證數據的原子性。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Java多線程學習(二)synchronized關鍵字(1) java多線程學習(二)synchroniz...
摘要:我的是忙碌的一年,從年初備戰實習春招,年三十都在死磕源碼,三月份經歷了阿里五次面試,四月順利收到實習。因為我心理很清楚,我的目標是阿里。所以在收到阿里之后的那晚,我重新規劃了接下來的學習計劃,將我的短期目標更新成拿下阿里轉正。 我的2017是忙碌的一年,從年初備戰實習春招,年三十都在死磕JDK源碼,三月份經歷了阿里五次面試,四月順利收到實習offer。然后五月懷著忐忑的心情開始了螞蟻金...
閱讀 2812·2019-08-30 15:55
閱讀 2858·2019-08-30 15:53
閱讀 2296·2019-08-26 13:47
閱讀 2558·2019-08-26 13:43
閱讀 3157·2019-08-26 13:33
閱讀 2805·2019-08-26 11:53
閱讀 1798·2019-08-23 18:35
閱讀 801·2019-08-23 17:16