Servlet單例多線程分析

Servlet的生命週期

1.      Servlet在web服務器啓動時被加載並實例化,容器運行其init方法初始化,請求到達時運行其service方法;

2.      service運行請求對應的doXXX(doGet,doPost)方法;

3.      服務器銷燬實例,運行其destory方法;


Servlet的生命週期由Servlet容器管理

(三個概念的理解:Servlet容器<Web容器<應用服務器

  Servlet容器的主要任務就是管理Servlet的生命週期;

  Web容器也稱之爲web服務器,主要任務就是管理和部署web應用的;

  應用服務器的功能非常強大,不僅可以管理和部署web應用,也可以部署EJB應用,實現容器管理的事務等等。。。

  Web服務器就是跟基於HTTP的請求打交道,而EJB容器更多是跟數據庫,事務管理等服務接口交互,所以應用服務器的功能是很多的。

  常見的web服務器就是Tomcat,但Tomcat同樣也是Servlet服務器;

  常見的應用服務器有WebLogic,WebSphere,但都是收費的;

  沒有Servlet容器,可以用Web容器直接訪問靜態Html頁面,比如安裝了apache等;如果需要顯示Jsp/Servlet,就需要安裝一個Servlet容器;但是光有servlet容器也是不夠的,它需要被解析爲html顯示,所以仍需要一個web容器;所以,我們常把web容器和Servlet容器視爲一體,因爲他們兩個容器都有對方的功能實現了,都沒有獨立的存在了,比如tomcat!


Servlet是如何處理多個請求同時訪問呢?

Servlet容器默認是採用單實例多線程的方式處理多個請求的

1.      當web服務器啓動的時候(或客戶端發送請求到服務器時),Servlet就被加載並實例化(只存在一個Servlet實例);

2.      容器初始化Servlet。主要就是讀取配置文件(例如tomcat,可以通過servlet.xml的<Connector>設置線程池中線程數目,初始化線程池;通過web.xml,初始化每個參數值等等);

3.      當請求到達時,Servlet容器通過調度線程(Dispatchaer Thread)調度它管理下的線程池中等待執行的線程(Worker Thread)給請求者;

4.      線程執行Servlet的service方法;

5.      請求結束,放回線程池,等到被調用;

從上面可以看出:

第一:Servlet單實例,減少了產生servlet的開銷;

第二:通過線程池來響應多個請求,提高了請求的響應時間;

第三:Servlet容器並不關心到達的Servlet請求訪問的是否是同一個Servlet還是另一個Servlet,直接分配給它一個新的線程;如果是同一個Servlet的多個請求,那麼Servlet的service方法將在多線程中併發的執行;

第四:每一個請求由ServletRequest對象來接受請求,由ServletResponse對象來響應該請求;


問題出現了:

同一個Servlet的的多個請求到來時,如果該Servlet中存在成員變量,可能發生多線程同時訪問該資源時,都來操作它,造成數據的不一致,因此產生線程安全問題。

解決:

1.      實現SingleThreadModel接口

如果一個Servlet被這個接口指定,那麼在這個Servlet中的service方法將不會有兩個線程被同時執行,當然也就不存在線程安全的問題;

2.      同步對共享數據的操作

使用synchronized關鍵字能保證一次只有一個線程可以訪問被保護的區段,Servlet可以通過同步塊操作來保證線程的安全。

ServletRequest對象是線程安全的,但是ServletContext和HttpSession不是線程安全的;

要使用同步的集合類:Vector代替ArrayList,HsahTable代替HashMap;

3.      避免使用實例變量(成員變量)

線程安全問題是由實例變量造成的,只要在Servlet裏面的任何方法裏面都不使用實例變量,那麼該Servlet就是線程安全的。(所有建議不要在servlet中定義成員變量,儘量用局部變量代替)

對上面的三種方法進行測試,可以表明用它們都能設計出線程安全的Servlet程序。但是,如果一個Servlet實現了SingleThreadModel接口,Servlet引擎將爲每個新的請求創建一個單獨的Servlet實例,這將引起大量的系統開銷。SingleThreadModel在Servlet2.4中已不再提倡使用;同樣如果在程序中使用同步來保護要使用的共享的數據,也會使系統的性能大大下降。這是因爲被同步的代碼塊在同一時刻只能有一個線程執行它,使得其同時處理客戶請求的吞吐量降低,而且很多客戶處於阻塞狀態。另外爲保證主存內容和線程的工作內存中的數據的一致性,要頻繁地刷新緩存,這也會大大地影響系統的性能。所以在實際的開發中也應避免或最小化Servlet中的同步代碼;在Serlet中避免使用實例變量是保證Servlet線程安全的最佳選擇。從Java內存模型也可以知道,方法中的臨時變量是在棧上分配空間,而且每個線程都有自己私有的棧空間,所以它們不會影響線程的安全。

 Servlet的線程安全問題只有在大量的併發訪問時纔會顯現出來,並且很難發現,因此在編寫Servlet程序時要特別注意。線程安全問題主要是由實例變量造成的,因此在Servlet中應避免使用實例變量。如果應用程序設計無法避免使用實例變量,那麼使用同步來保護要使用的實例變量,但爲保證系統的最佳性能,應該同步可用性最小的代碼路徑。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章