Servlet生命週期與線程安全

        上一篇介紹了Servlet初始化,以及如何處理HTTP請求,實際上在這兩個過程當中,都伴隨着Servlet的生命週期,都是Servlet生命週期的一部分。同時,因爲Tomcat容器默認是採用單實例多線程的方式處理多個請求,這一特性就致使了線程安全問題的存在。所以,本篇主要講述Servlet生命週期與線程安全問題。html

        一、Servlet生命週期java

        Servlet是運行在容器當中的,因此其生命週期也由容器控制,最經常使用的容器就是Tomcat,筆者經歷過的全部項目也都是以Tomcat做爲Servlet的容器。通過前面幾篇介紹,相信你們對Servlet的生命週期有了必定的瞭解。Servlet的生命週期實際上是經過javax.servlet.Servlet接口中的init()、service()、destroy()等方法表示的,主要有四個階段組成:加載並實例化、初始化(init())、處理請求(service())、銷燬(destroy())。下面分別介紹這四個階段。web

        加載並實例化安全

        Tomcat容器負責Servlet的加載並實例化,其實例化分兩種狀況:當web.xml文件裏配置了<load-on-startup>標籤而且裏面的數字>=0時,容器啓動時即加載Servlet類並建立類的實例;若是未配置<load-on-startup>標籤或數字<0時,容器啓動時不會加載Servlet類,固然也就不會建立類的實例。這時,當用戶首次訪問Servlet類時會加載並實例化。不管採用哪一種方式實例化,都只會建立一個類的實例,不管多少用戶訪問Servlet,都共用這一個實例。服務器

        初始化(init())微信

        在Servlet類實例化以後,容器將調用init()方法,傳遞ServletConfig接口的對象,進行初始化。在init()方法中,能夠經過getServletConfig()方法獲取ServletConfig對象,而後經過此對象的getInitParameter()等方法獲取web.xml文件中<init-param>標籤裏面的配置信息,並對配置信息進行解析,或者執行任何其餘一次性活動。在Servlet的整個生命週期中,init()方法只會被執行一次。多線程

        處理請求(service())併發

        在Servlet初始化完成以後,容器就準備接收並處理客戶的請求了。處理請求時,容器會調用Servlet的service(HttpServletRequest req, HttpServletResponse resp)方法,這個方法會判斷用戶發送的請求類型,是「POST」請求仍是「GET」請求或是其餘請求,而後根據請求類型執行相應的doPost()方法、doGet()方法或其餘方法。Tomcat容器會將用戶請求的數據封裝到HttpServletRequest對象中,服務器處理完用戶請求以後,將結果信息返回到HttpServletResponse對象中,最終這兩個對象做爲參數傳遞到doPost()、doGet()或其餘方法中,將結果信息返回到頁面顯示。當多個客戶的請求到來時,服務器會建立多個線程,每一個客戶請求對應一個線程,每一個請求的service()方法都能運行在本身獨立的線程中。性能

        銷燬(destroy())學習

        當Tomcat容器關閉時或因爲其餘緣由致使Servlet須要關閉或卸載時,容器會調用該對象的destroy()方法,以便讓Servlet對象能夠釋放它所使用的資源,該方法一樣只會執行一次。在容器調用destroy()方法前,若是還有其餘的線程正在service()方法中執行,容器會等待這些線程執行完畢或者等待服務器設定的超時值到達。一旦Servlet對象的destroy()方法被調用,容器會釋放這個Servlet對象,在隨後的時間內,該對象會被java的垃圾收集器所回收。這四個階段共同組成了Servlet的生命週期。

        二、Servlet線程安全

        經過上面的Servlet生命週期能夠看出,在Tomcat容器加載並實例化Servlet以後,會建立一個實例,而且這個實例是惟一的,不管多少用戶訪問Servlet,都共用這一個實例。而每次用戶訪問Servlet時,服務器都會爲每一個用戶建立一個獨立的線程,每一個線程都有它本身的堆棧空間。因此說是單實例多線程,這種默認以多線程方式執行的設計可大大下降對系統的資源需求,提升系統的併發量及響應時間,但也同時引起了Servlet的線程安全問題。

        對於Servlet中的局部變量,多線程下每一個線程對局部變量都會有本身的一份copy,存在本身的堆棧空間中,這樣對局部變量的修改只會影響到本身的copy而不會對別的線程產生影響,因此這是線程安全的;對於Servlet中的實例(全局)變量,多線程下全部線程共享實例變量,這一共享就可能致使多個線程之間互相影響,從而引起線程的不安全。

        知道了引起線程不安全問題的緣由,那麼該如何預防這一狀況發生呢?

        不使用實例變量

        既然實例變量能引起線程安全問題,那麼只要在Servlet類的任何方法裏面都不使用實例變量,該Servlet就是線程安全的。事實上,線程安全問題大部分是由實例變量形成的,在Servlet中避免使用實例變量是保證Servlet線程安全的最佳選擇。

        使用synchronized

        synchronized關鍵字能保證一次只有一個線程能夠訪問被保護的區段,因此理論上能夠經過同步塊操做來保證Servlet的線程安全。但由於其「一次只有一個線程能夠訪問」的特性,致使當大量用戶訪問同一資源時,只能排隊訪問,大量用戶處於阻塞狀態,這就大大下降了其用戶的吞吐量,從而使系統的效率和性能大大下降,不推薦使用此方法。

        其餘方式

        Java的有些集合類也會引起線程安全問題,應避免使用。好比用Vector代替ArrayList,用Hashtable代替HashMap等。另外,不要在Servlet中建立其餘線程來完成某個功能,由於Servlet自己就是多線程的,再在Servlet中建立線程,更容易引起線程安全問題。

        轉載請註明出處 http://www.cnblogs.com/Y-oung/p/8433426.html

        工做、學習、交流或有任何疑問,請聯繫郵箱:yy1340128046@163.com  微信:yy1340128046

相關文章
相關標籤/搜索