服務端線程模型-線程池服務模型


單線程服務器

初學網絡編程時,我們寫的服務端的代碼大部分如下所示。

在一個循環中等待客戶端請求,一旦接到請求就在當前線程與客戶端進行通信,這就是單線程服務模型。

這種模型有個問題,就是當請求量一上來,同時第二步的操作耗時過長時,許多請求就會阻塞在系統的Socket隊列中,無法及時得到處理,響應時間增加,嚴重會導致系統拒接請求(Socket隊列溢出),直接影響用戶體驗。

private void service() {

    while (true) {

        // 1.接到一個客戶端請求
        Socket socket = serverSocket.accept();

        // 2.從socket中獲取輸入輸出流,與客戶端進行通信
        InputStream in = socket.getInputStream();
        OutputStream out = socket.getOutputStream();
    }
}

 

 

多線程服務模型

為了應對單線程服務器的缺陷,自然而然就是每來一個請求都開一個線程去處理,偽代碼如下。這個模型能同時處理多個請求,每來一個請求就開一個線程去處理,對每個客戶都給予快速的響應不阻塞。但是不足之處也很明顯:

每來一個請求就創建一個線程,如果請求量大的話,創建和銷毀的線程就會非常多,服務器創建和銷毀線程的開銷將巨增;

頻繁的創建和銷毀線程,會導致頻繁的上下文切換,從而影響服務器性能;

每一個線程都是會占用內存資源的,大量請求意味着大量的內存要拿去開辟這種線程,可能會導致系統的內存空間不足;

private void service() {

    while (true) {

        // 1.接到一個客戶端請求
        Socket socket = serverSocket.accept();

        // 2.開啟一個線程去處理請求
        new Thread(new ServiceTask(socket)).start();
    }
}

 

 

 

線程池服務模型

單純的多線程服務模型有個問題就是開的線程太多,嚴重影響系統性,但是不開的話,請求處理又不及時。

有沒有辦法可以既不用開太多線程,又能保證請求被及時處理呢?

這時可以在服務端設置一個請求隊列,同時開適當數量的服務線程,這些服務線程不斷的從請求隊列中拿請求去進行處理,這個模型的核心思想就是通過復用線程,從而減少創建線程和銷毀線程帶來的系統開銷,這就是線程池服務模型(Master-Worker模式)。 

private void service() {

    // 1.創建一個線程池
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    while (true) {

        // 1.接到一個客戶端請求
        Socket socket = serverSocket.accept();

        // 2.將請求方式請求隊列中,等待服務線程處理
        executorService.execute(new ServiceTask(socket));
    }
}
 
 

 

使用線程池服務模型應該注意的點

線程太多:如果線程數開的太多,這些線程會消耗包括內存在內的其他系統資源,影響系統性能。所以應當根據具體情況開合適的線程數

請求太多:請求太多意味着請求隊列過大,同樣也會占用過多的系統資源。所以應當設置適當的拒絕策略,保護系統的同時不過與影響用戶體驗

線程泄漏:如果服務線程阻塞了,比如等待用戶請求,比如死鎖。這個服務線程就無法去處理請求,線程池將會失去這個“勞動力”,如果接二連三所有的線程都因為某個原因而無法處理請求,線程池終將沒有剩余的工作線程去處理隊列中的請求,這就是線程泄漏,所以,應當在阻塞的地方設置合理的最大阻塞時間避免永久的阻塞,同時避免死鎖。

引用

1.《Java網絡編程精解》(孫衛琴)


免責聲明!

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



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