在瞭解Web服務器如何保證併發線程的獨立性以前,咱們先了解一下Web服務器處理併發請求的工做模型php
- 單線程的Web服務器,也即Servlet是單例模式。Web服務器一次處理一個請求,結束後讀取並處理下一個請求。在某請求處理過程當中,其餘請求將被掛起,所以在併發量較大的場景中將會出現嚴重的性能問題。
- 多進程/多線程Web服務器。這種服務器生成多個進程或線程並行處理多個用戶請求,進程或線程能夠事先生成,也能夠按需生成。這種服務器能夠爲每一個用戶請求生成一個單獨的進程或線程來進行相應,不過,一旦併發請求達到成千上萬時,多個同事運行的進程或線程將會大量消耗系統資源,而且新建一個線程,銷燬一個線程的開銷是很大的,它時常會大於實際處理請求自己的額開銷,所以這種方式並不能充分利用計算機資源,提高併發的效率也是有限的,若是碰到線程安全的問題,須要使用到線程的鎖機制,那併發提高就會受到更大的限制。
- 線程池策略Web服務器。爲了改善以上兩個web服務器架構,不少服務器利用了線程池技術。線程池技術就是事先建立一批線程,這批線程在到達服務器以前,這些線程處於待命狀態,當請求到達後,程序從線程池中去除一個線程,這個線程處理到達的請求,請求處理完畢,該線程被線程池回收。通常地,一個線程對應一個請求,當線程數達到服務器所能處理的最大數量,則線程掛起等待或者啓用線程池拒絕策略(請求實在太多了,線程池處理不過來了,JDK的線程池提供了一個隊列機制,讓這些請求排隊等待,這樣避免請求丟失。若是隊列容量超過了計算機處理能力,隊列會拋棄沒法處理的請求,這個叫作線程池的拒絕策略)
- I/O多路複用Web服務器。這個服務器原理我尚未仔細研究過,有興趣的你抽時間能夠問問度娘。其實實現一個非阻塞的請求是個大課題,裏面使用到了不少先進和複雜的技術例如:回調函數和輪詢等,對於非阻塞的開發我目前掌握的還不夠好,等我有天徹底掌握了它我必定會再寫一篇文章,不過這裏要提到的是像java裏netty技術,nginx,php的併發處理都用到這種機制的原理,特別是如今很火的nodejs它產生的緣由就是依靠這種非阻塞的技術來編寫更高效的web服務器,能夠說nodejs把這種技術用到了極致,不過這裏要糾正下,非阻塞是針對IO操做的技術,對於nodejs,netty的實現機制有更好的術語描述就是事件驅動(其實就是使用回調函數,觀察者模式實現的)以及異步的IO技術(就是非阻塞的IO技術)
如今瞭解了Web服務器處理併發請求的工做模型,那咱們接下來接着談一下Web服務器併發環境下如何保證線程獨立。線程獨立也就是線程安全。java
線程安全的定義:在多線程環境下可以正確執行的代碼就是線程安全的。在單線程環境下,運行程序總能返回惟一結果。node
Servlet是多線程環境下的,即有可能有多個請求發給一個Servlet實例,每一個請求就是一個線程。在JavaWeb服務器環境下併發,要注意的安全問題最簡單的實現方法就是不要再Servlet或者Struts的Action中使用類變量或者實例變量。若是有這些變量,能夠將它們轉化爲方法的參數傳入,以消除它們。被Servlet或Action調用的類中(如值對象、領域模型類)中是否能夠安全的使用實例變量?若是你在每次方法調用時新建一個對象,再調用它們的方法,則不存在同步問題---由於它們不是多個線程共享的資源,只有共享的資源才須要同步---而Servlet和Action的實例對於多個線程是共享的。換句話說,Servlet和Action的實例會被多個線程同時調用,而過了這一層,若是在你本身的代碼中沒有另外啓動線程,且每次調用後續業務對象時都是先新建一個實例再調用,則都是線程安全的。nginx