java異步編程


Q:為何要采用異步編程

A:異步編程首先不會節約線程,因為異步操作都會重新開一個線程。異步編程是提高了CPU的使用率,采用同步編程的方式,整個服務器的所有線程大部分都沒有在工作,而是在等待。因為線程同步操作

要等整個事件處理完成才能提交,所以CPU的利用率很低;當采用異步編程,線程不需要等待,減少時間分片的占用,提高系統的吞吐量。在超高請求數量場景下,異步的實現不再需要線程等待執行結果,只需要個位數量的線程,即可實現同步場景大量線程一樣的吞吐量。

 

舉例:

   實現一個轉賬的微服務 Transfer( accountFrom, accountTo, amount),這個服務有三個參數:

分別是轉出賬戶、轉入賬戶和轉賬金額。

  實現過程也比較簡單,我們要從賬戶 A 中轉賬 100 元到賬戶 B 中:先從 A 的賬戶中減去 100 元;再給 B 的賬戶加上 100 元,轉賬完成。

  采用同步操作:

  


Transfer(accountFrom, accountTo, amount) {
// 先從accountFrom的賬戶中減去相應的錢數
Add(accountFrom, -1 * amount)
// 再把減去的錢數加到accountTo的賬戶中
Add(accountTo, amount)
return OK
}

 

  同步操作我們很容易想到的,那同步操作的效率怎么樣呢? 

  假設內部add方法執行效率在50ms,那一個線程執行燒水泡茶的時延在100ms,按照這種實現,每個線程每分鍾最多處理10個請求,假設我們使用的服務器同時打開的線程數量上限是 10,000,
可以計算出這台服務器每秒鍾可以處理的請求上限是: 10,000 (個線程)* 10(次請求每秒) = 100,000 次每秒。如果請求速度超過這個值,那么請求就不能被馬上處理,只能阻塞或者排隊,
這時候 Transfer 服務的響應時延由 100ms 延長到了:排隊的等待時延 + 處理時延 (100ms)。也就是說,在大量請求的情況下,我們的微服務的平均響應時延變長了。 這已經到CPU的極限了嗎?
其實不然,如果我們監測一下服務器的各項指標,會發現無論是 CPU、內存,還是網卡流量或者是磁盤的 IO 都空閑的很,多大部分線程都在等待每一個服務返回結果。

  
  

  采用異步操作:

   是在線程模型上由同步順序調用改為了異步調用和回調的機制。

  接下來,我們來看下,如何用 CompletableFuture 實現的轉賬服務。

  首先,我們用 CompletableFuture 定義 2 個微服務的接口:

  

/**
 * 賬戶服務
 */
public interface AccountService {
    /**
     * 變更賬戶金額
     * @param account 賬戶ID
     * @param amount 增加的金額,負值為減少
     */
    CompletableFuture<Void> add(int account, int amount);
}

/**
 * 轉賬服務
 */
public interface TransferService {
    /**
     * 異步轉賬服務
     * @param fromAccount 轉出賬戶
     * @param toAccount 轉入賬戶
     * @param amount 轉賬金額,單位分
     */
    CompletableFuture<Void> transfer(int fromAccount, int toAccount, int amount);
}

/**
 * 轉賬服務的實現
 */
public class TransferServiceImpl implements TransferService {
    @Inject
    private  AccountService accountService; // 使用依賴注入獲取賬戶服務的實例
    @Override
    public CompletableFuture<Void> transfer(int fromAccount, int toAccount, int amount) {
      // 異步調用add方法從fromAccount扣減相應金額
      return accountService.add(fromAccount, -1 * amount)
      // 然后調用add方法給toAccount增加相應金額
      .thenCompose(v -> accountService.add(toAccount, amount));    
    }
}

  我們先調用一次賬戶服務 accountService.add() 方法從 fromAccount 扣減響應的金額,因為 add() 方法返回的就是一個 CompletableFeture 對象,可以用 CompletableFeture 的 thenCompose() 方法將下一次調用 accountService.add() 串聯起來,實現異步依次調用兩次賬戶服務完整轉賬。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM