2017-03-20 82 views
0

我想了解的Java異步的好處。爪哇 - 異步 - 線程池

方案1: 我有部署到Tomcat的彈簧引導web應用程序,使用Tomcat min和max線程都設定在200

@Service 
public class MyService{ 

    public String execute(){ 
     try { 
      //simulate blocking 
      Thread.sleep(3000); 
     } catch (InterruptedException e) {} 
     return "OK";  
    } 
} 

@RestController 
public class MyController { 

    @Autowired 
    private MyService service; 

    @RequestMapping("/test") 
    public String test(){ 
     return service.execute(); 
    } 
} 

方案2:我有部署到Tomcat的彈簧引導web應用程序與Tomcat的最小和最大線程均設定爲100

@Service 
public class MyService{ 

    public String execute(){ 
     try { 
      //simulate blocking 
      Thread.sleep(3000); 
     } catch (InterruptedException e) {} 
     return "OK";  
    } 
} 

@RestController 
public class MyController { 

    @Autowired 
    private MyService service; 

    private ExecutorService executorService = Executors.newFixedThreadPool(100); 

    @RequestMapping("/test") 
    public DeferredResult<String> test(){ 
     DeferredResult<String> deferredResult = new DeferredResult<>(); 
     CompletableFuture.supplyAsync(service::execute, executorService). 
      whenCompleteAsync((result, throwable) -> deferredResult.setResult(result));  
     return deferredResult;  
    } 
} 

在每個場景中,線程的總數爲200

但我不明白如何風光ario 2會有更好的表現:

在場景1中,如果有400個請求同時進入,前200個將由200個http線程提供服務,接下來的200個將等待3秒(再加上一點),直到其中一個線程再次可用。

所以可以通過爲每6秒400個請求=每秒66.6請求。

的平均響應時間是(200 * 3 + 200 * 6)/(400)=4.5秒

在方案2中,如果 400個請求在同一時間進入。前100個將由100個http線程立即提供服務,這些線程中的每一個都會調用該服務,而不是等待結果,然後立即恢復,並可用於接下來的100個請求。 但現在用於第二100個請求,當每個HTTP線程調用服務,該服務目前等待3秒(減去一個位)來完成處理前100個線程。所以下一個100隊列(在executorservice的線程池中)。 因此,幾乎沒有任何時間,我們處理了所有400個請求,但 100正在服務中處理(等待3秒鐘),而300個正在執行程序服務線程池中排隊。 3秒後,前100個完成,接下來100個出隊並處理,等等。

所以吞吐量是在12秒內400個請求=每秒

的平均響應時間是 (100 * 3 + 100 * 6 + 100 * 9 + 100 * 12)/(400)= 7.5 33.3請求秒

現在,有人可能會說'我可以通過增加執行程序服務線程池中的線程數來改進方案2',我可以回覆'好的,然後我可以增加tomcat中的線程數在場景1中的池數量相同'

回答

0

要在此場景中查看異步的優點,您需要mak e您的服務也是異步的。三秒鐘後完成運行計劃後,它會立即返回,而不是執行關聯該線程的睡眠。在這種情況下,所有請求將在三秒鐘內完成;吞吐量爲每秒133次,平均響應時間爲3秒。如果您調整線程數量,您將擁有基本相同的響應時間。

異步點在於你的空閒I/O等待的線程可以立即自由地做其他事情,所以你不必使用很多線程,這是一個昂貴的資源,可以滿足你的工作量。

+0

感謝您的回覆。 但是,如果服務也是異步並立即返回,哪個線程正在等待完成可完成未來的響應 –

+0

API的調用者仍在等待「OK」響應。有些線程必須返回該響應,即使它不是tomcat http線程。 –

+0

@ jonathan.stiles - 當計時器任務的時間耗盡時,任務被分配給線程池上的空閒線程,或者如果沒有空閒線程,則將其分配給下一個線程中服務的隊列游泳池變得閒置。線程池中的這個線程最終完成DeferredResult並將響應發送給客戶端。在適當的異步中,所有線程都要麼完成工作,要麼在等待調度的線程池中處於空閒狀態。 – antlersoft

0

在你的問題中你有一個非常綜合的情況。

比方說,你們兩個都有10個線程(10個HTTP線程和異步版本的5 + 5個線程),並且你的程序不僅僅是調用睡眠方法。但是,80%的請求確實需要3秒的操作(比方說數據庫查詢)。

現在,在這兩種情況下,您已經設法同時獲得所有線程來調用阻塞方法。到目前爲止,沒有太大的區別。如果另一個呼叫進入阻塞方法,它將不得不等待。

現在,突然間,您得到了一個不同操作的請求,比如說登錄。登錄很簡單,只需檢查數據庫中的一行。在第一種情況下,它必須等待3秒鐘,因爲沒有可用的HTTP線程來處理它。在第二種情況下,你有一個完全不相關的線程池,但由於你沒有使用它來登錄,所以你的登錄請求會立即被服務。

好的,爲什麼不使用DeferredResult創建一個1000線程池?線程很昂貴。你不希望遇到你已經設法得到1000個執行一些昂貴任務的線程的情況,你的CPU是100%,而不是3秒,每個請求的運行時間變爲30秒。您的服務器扼流圈和吞吐量將變爲0.

這也適用於連接池。少即是多。

+0

我認爲這可能不是一個公平的比較,''兩個'10線程' 如果每個都有10個http線程,但方案2也有一個executorservice來處理異步服務,那麼方案2實際上有更多的線程。 –

+0

沒關係。一個對所有請求都有X個通用線程,另一個對於特定的長時間運行(其中Y + Z = X)有Y個通用線程和Z個線程。第二個允許更好的吞吐量。你的例子太糟糕了(並且完全不基於現實),它讓你感到困惑。優點來自*不是所有的請求都是異步的,而是爲那些需要花費大量時間等待的人自己管理。 – Kayaman