2013-05-15 33 views
3

這不是一項家庭作業,而是我在網上找到的一個採訪問題。滿足高併發性的簡單Web服務器

的Java代碼:

public class SimpleWebServer{ 
    public static void handleRequest(Socket c) 
    { 
    //Process the request 
    } 

    public static void main(String[] args) throws IOException 
    { 
    ServerSocket server=new ServerSocket(80); 
    while(true) 
    { 
     final Socket connection=server.accept(); 
     Runnable task=new Runnable(){ 
      @Override 
      public void run() 
      { 
      handleRequest(connection); 
      } 
     }; 
     new Thread(task).start(); 
    } 
    } 
} 

的問題是,當有高併發會有什麼潛在的問題?我的分析是:

  1. 它沒有使用synchronized關鍵字,所以可能會出現競態條件發生的情況。
  2. 它應該使用線程池,這是更高效。
  3. 對於每個傳入線程來說,這個類總是會創建一個新的ServerSocket,當高併發性發生時這將消耗大量空間?

回答

4

我看到的主要問題就是您確定的問題。 thread-per-request模型本質上是有缺陷的(正如Nginx和Apache上的lighttpd的廣泛採用所證明的那樣)。轉移到ExecutorService(可能由線程池支持)將是一個不錯的選擇。

通過將線程每請求更改爲將任務簡單提交給ExecutorService,您將此應用程序轉向基於事件的模型。網上有很多材料宣揚基於線程模型的基於事件的可伸縮性優點。

handleRequest方法中使用'synchronized'是一種非常蠻力的策略,並且根據方法的特定內容,更細粒度的鎖定策略(或無鎖邏輯)將是優選的。您提到的ServerSocket創建只會發生一次,因此這不是一個可伸縮性問題。方法accept確實爲每個連接創建了一個新的Socket實例,但這些實例很便宜。查看JDK 6源代碼,這包括分配7個布爾值,鎖Object,並檢查一些內部狀態 - 即可能不會成爲問題。

基本上,你是在正確的軌道上!希望這可以幫助。

0

下面是產生線程潛在問題

1)列表採用CPU週期和每個線程有其消耗存儲器它自己的數據結構。當一個線程阻塞時,JVM保存其狀態並選擇另一個線程運行,並恢復所選線程的狀態,它被稱爲上下文切換。隨着線程數量的增加,線程會消耗更多資源。通過限制線程數量和重用線程而不是產生新線程來引入線程池來解決這個問題。

2)最令人沮喪和重要的問題是讓這些線程以同步方式共享或訪問變量(例如狀態)。很容易使這些變量不一致。因此,一些程序員更喜歡使用單線程環境(例如NIO)而不是多線程環境。

3)沒有日誌記錄系統會讓線程環境更難調試,當你想要追查一個問題。

4)當你想接受一個新的客戶端時,你不需要實例化新的serversocket(),因爲它是一個共享的Serversocket實例。