CompletebaleFuture的底層原理是:Fork/joinPoll + Treiber stack(異步任務(wù)棧)+CAS蝙眶,可以實(shí)現(xiàn):創(chuàng)建較少的線程(減少線程上下文切換)執(zhí)行較多的任務(wù)(不耗時(shí)的任務(wù))
結(jié)論:當(dāng)任務(wù)不是很耗時(shí)靡狞,多線程的作用不大,反而性能上比不上單線程拘央。而在多線程中涂屁,推薦CompletableFuture去創(chuàng)建任務(wù)、開啟線程操作灰伟,性能比用Callable與FutureTask組合好很多拆又。
同步調(diào)用的缺點(diǎn)
我們假設(shè)一個(gè)電子商城用戶購(gòu)買商品的場(chǎng)景: 創(chuàng)建訂單前的驗(yàn)證方法。
/**
* 驗(yàn)證訂單是否合法
*
* @param userId 用戶id
* @param itemId 商品id
* @param discount 折扣
* @return
*/
public boolean verifyOrder(long userId, long itemId, double discount) {
// 驗(yàn)證用戶能否享受這一折扣栏账,RPC調(diào)用
boolean verifyDiscount = discountService.verify(userId, itemId, discount);
if(!verifyDiscount) {
// 該用戶無(wú)法享受這一折扣
return false;
}
// 獲取商品單價(jià)帖族,RPC調(diào)用
double itemPrice = storeService.getPrice(itemId);
// 用戶實(shí)際應(yīng)該支付的價(jià)格
double realPrice = itemPrice * discount;
// 獲取用戶賬號(hào)余額,限定了只能使用余額購(gòu)買发笔,RPC調(diào)用
double balance = userService.getBalance(userId);
return realPrice <= balance;
}
這個(gè)方法里面涉及到了 3 個(gè) rpc 調(diào)用盟萨,假設(shè)每個(gè) rpc 調(diào)用都需要 10ms凉翻,那么
verifyOrder 這個(gè)方法總耗時(shí)將不低于 30ms了讨。
在同步調(diào)用系統(tǒng)中,延遲同時(shí)會(huì)導(dǎo)致吞吐量的下降制轰。如果只有一個(gè)線程前计,那么系統(tǒng)每秒的吞吐量將不會(huì)高于 1000ms / 30ms,也就是最多 33 qps垃杖。同步系統(tǒng)要提高吞吐量男杈,唯一的辦法就是加大線程數(shù)。同時(shí)啟用 1,000 個(gè)線程调俘,吞吐量理論值可以上升到 33,333 qps伶棒。不過(guò)實(shí)際使用中彩库,這并不是完美的方案:增加線程數(shù)量會(huì)導(dǎo)致頻繁的上下文切換肤无,系統(tǒng)整體性能將會(huì)嚴(yán)重下降。
Future 的不足
為了解決同步系統(tǒng)的問題骇钦,Java 5 引入了 Future宛渐。有了 Future 后,上面的方法可以修改為:
/**
* 驗(yàn)證訂單是否合法
*
* @param userId 用戶id
* @param itemId 商品id
* @param discount 折扣
* @return
*/
public boolean verifyOrder(long userId, long itemId, double discount) {
// 驗(yàn)證用戶能否享受這一折扣窥翩,RPC調(diào)用
Future<Boolean> verifyDiscountFuture = discountService.verify(userId, itemId, discount);
// 獲取商品單價(jià)寇蚊,RPC調(diào)用
Future<Double> itemPriceFuture = storeService.getPrice(itemId);
// 獲取用戶賬號(hào)余額笔时,限定了只能使用余額購(gòu)買,RPC調(diào)用
Future<Double> balanceFuture = userService.getBalance(userId);
if(!verifyDiscountFuture.get()) {
// 該用戶無(wú)法享受這一折扣
return false;
}
// 用戶實(shí)際應(yīng)該支付的價(jià)格
double realPrice = itemPriceFuture.get() * discount;
// 用戶賬號(hào)余額
double balance = balanceFuture.get();
return realPrice <= balance;
}
CompletableFuture 才是王道
引入 CompletableFuture 后仗岸,我們可以使用如下形式:
/**
* 驗(yàn)證訂單是否合法
*
* @param userId 用戶id
* @param itemId 商品id
* @param discount 折扣
* @return
*/
public CompletableFuture<Boolean> verifyOrder(long userId, long itemId, double discount) {
// 驗(yàn)證用戶能否享受這一折扣糊闽,RPC調(diào)用
CompletableFuture<Boolean> verifyDiscountFuture = discountService.verify(userId, itemId, discount);
// 獲取商品單價(jià),RPC調(diào)用
CompletableFuture<Double> itemPriceFuture = storeService.getPrice(itemId);
// 獲取用戶賬號(hào)余額爹梁,限定了只能使用余額購(gòu)買右犹,RPC調(diào)用
CompletableFuture<Double> balanceFuture = userService.getBalance(userId);
return CompletableFuture
.allOf(verifyDiscountFuture, itemPriceFuture, balanceFuture)
.thenApply(v -> {
if(!verifyDiscountFuture.get()) {
// 該用戶無(wú)法享受這一折扣
return false;
}
// 用戶實(shí)際應(yīng)該支付的價(jià)格
double realPrice = itemPriceFuture.get() * discount;
// 用戶賬號(hào)余額
double balance = balanceFuture.get();
return realPrice <= balance;
});
}
延遲降低為原來(lái) 1/3,同時(shí)吞吐量也不會(huì)因?yàn)檠舆t而降低姚垃。非常完美念链,簡(jiǎn)單高效,CompletableFuture 絕對(duì)稱得上是大殺器积糯。在 rpc 異步調(diào)用這個(gè)問題上掂墓,沒什么比 CompletableFuture 更適合的解決方案了。CompletableFuture 是 Doug Lea 的又一力作看成,徹底解決了 Future 的缺陷君编,把 Java 帶入了異步響應(yīng)式編程的新世界。
參考:
https://blog.csdn.net/jack_shuai/article/details/115304267