簡體   English   中英

Java - 異步 - 線程池

[英]Java - Async - Thread pool

我試圖了解java中async的好處。

場景1:我有一個部署到tomcat的spring boot web app,tomcat min和max threads都設置為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的spring boot web app,tomcat min和max threads都設置為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。

但我不知道方案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個請求=每秒33.3個請求

平均響應時間為(100 * 3 + 100 * 6 + 100 * 9 + 100 * 12)/(400)= 7.5秒

現在,有人可能會爭辯說,'我可以通過增加執行程序服務線程池中的線程數來改進方案2',我可以回復,'好吧,那么我可以增加方案1中tomcat池中的線程數相同金額'

要在此方案中查看async的優點,您需要使服務也異步。 它不是做一個阻塞線程的Sleep,而是在三秒鍾后完成調度運行后立即返回。 在這種情況下,所有請求將在不到三秒的時間內完成; 您的吞吐量為每秒133個請求,平均響應時間為3秒。 如果您將線程數量調低,那么您的響應時間基本相同。

異步的要點是,等待I / O空閑的線程可以立即自由地執行其他操作,因此您不必使用盡可能多的線程(這是一種昂貴的資源)來滿足您的工作負載。

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

假設你有兩個線程(10個HTTP線程和5 + 5個線程用於異步版本),你的程序不僅僅是調用一個睡眠的方法。 但是,80%的請求確實涉及3秒鍾的操作(假設數據庫查詢)。

現在,在這兩種情況下,您已經設法同時獲得所有線程來調用阻塞方法。 到目前為止,沒有太大的區別。 如果阻塞方法有另一個調用,則必須等待。

現在,突然你得到了一個不同的操作請求,讓我們說登錄。 登錄很簡單,只檢查數據庫中的一行。 在第一種情況下,它必須等待3秒,因為沒有可用的HTTP線程來為它提供服務。 在第二種情況下,您有一個完全無關的線程池,但由於您沒有使用它進行登錄,因此您可以立即為您的登錄請求提供服務。

好的,那么為什么不在不使用DeferredResult情況下創建1000大小的線程池呢? 線程很貴。 您不希望遇到這樣的情況:您已經設法獲得1000個執行某些昂貴任務的線程,您的CPU處於100%而不是3秒,每個請求的運行時間變為30秒。 您的服務器阻塞和吞吐量變為0。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM