struts2的action是多例,servlet是單例

struts2中action是多例的,即一個session產生一個action
若是是單例的話,若出現兩個用戶都修改一個對象的屬性值,則會由於用戶修改時間不一樣,兩個用戶訪問獲得的java

屬性不同,操做得出的結果不同.
舉個例子:有一塊布長度300cm,能作一件上衣(用掉100cm)和一件褲子(用掉200cm);甲和乙同時訪問獲得的spring

長度都是300cm,
甲想作上衣和褲子,他先截取100cm去作上衣,等上衣作完再去作褲子,而乙這時正好也拿100cm去作上衣,那瀏覽器

好,等甲作完上衣再作褲子的時候發現剩下的布(100cm)已經不夠作褲子了.....這就是影響系統的性能,解決安全

的辦法就是給甲和乙一人一塊300cm的布,就不會出現布被別人偷用的事情,也是就單實例和多實例的區別
若是設置成單例,那麼多個線程會共享一個ActionContext和ValueStack,這樣併發訪問的時候就會出現問服務器

題了
struts 2的Action是多實例的並不是單例,也就是每次請求產生一個Action的對象。緣由是:struts 2的Action網絡

中包含數據,例如你在頁面填寫的數據就會包含在Action的成員變量裏面。若是Action是單實例的話,這些session

數據在多線程的環境下就會相互影響,例如形成別人填寫的數據被你看到了。因此Struts2的Action是多例數據結構

模式的。
問題出現了,可讓struts2的action變成單例模式麼?我在使用spring來生成action的時候,發現生成的多線程

action竟然全是單例的。這不是讓個人程序默認就跑出bug來麼?上個用戶提交的信息,若是下個用戶沒填併發

,竟然跑到上個用戶輸入的信息去了。
背景:
1) Struts2會對每個請求,產生一個Action的實例來處理.
2) Spring的Ioc容器管理的bean默認是單實例的.
首先從數據安全性的問題上考慮,咱們的Action應該保證是多例的,這樣纔不會出現數據問題。可是若是有

的action好比只有admin才能操做,或者某些action,全站公用一個來提升性能,這樣的話,就可使用單

例模式。
不過幸虧,Spring的bean能夠針對每個設置它的scope,因此,上面的問題就不是問題了。若是用單例,

就在spring的action bean配置的時候設置scope="prototype".好吧,問題到此結束。

而servlet採用單實例多線程模式開發,減小產生servlet實例的開銷。

servlet容器維護一個線程池,裏面放着工做者線程來相應請求,同時還有一個調度線程來管理工做者線程。當容器收到一個servlet請求,調度線程就從線程池中取出一個工做者線程,該工做者線程將處理這個請求,作法是執行servlet的service方法;當這個線程執行時,收到另外一個請求,調度線程就在線程池中取出另外一個工做者線程來響應新的請求。容器不關心請求是否訪問的是同一個servlet,當多個請求同時訪問同一個servlet時,這個servlet的service方法將在多線程中併發執行。併發執行必然會出現同步問題,也就是線程的安全問題,下面是網上找到的本來版本。繞來繞去,總也繞不開幾門基礎課,終於明白爲何考研要考數據結構,操做系統,組成原理和網絡原理了,這一個線程安全問題就涉及到數據結構和操做系統,數據結構幫助選擇線程安全的數據類型,操做系統幫助找到線程安全的操做方法。

一. Servlet容器如何同時來處理多個請求 
     工做者線程Work Thread:執行代碼的一組線程 
     調度線程Dispatcher Thread:每一個線程都具備分配給它的線程優先級,線程是根據優先級調度執行的 
     Servlet採用多線程來處理多個請求同時訪問。servlet依賴於一個線程池來服務請求。線程池其實是一系列的工做者線程集合。Servlet使用一個調度線程來管理工做者線程. 
     當容器收到一個Servlet請求,調度線程從線程池中選出一個工做者線程,將請求傳遞給該工做者線程,而後由該線程來執行Servlet的service方法。當這個線程正在執行的時候,容器收到另一個請求,調度線程一樣從線程池中選出另外一個工做者線程來服務新的請求,容器並不關心這個請求是否訪問的是同一個Servlet.當容器同時收到對同一個Servlet的多個請求的時候,那麼這個Servlet的service()方法將在多線程中併發執行。 
      Servlet容器默認採用單實例多線程的方式來處理請求,這樣減小產生Servlet實例的開銷,提高了對請求的響應時間,對於Tomcat能夠在server.xml中經過<Connector>元素設置線程池中線程的數目。 
     就實現來講: 
      調度者線程類所擔負的責任是線程的調度,只須要利用本身的屬性完成本身的責任。因此該類是承擔了責任的,而且該類的責任又集中到惟一的單體對象中。 
而其餘對象又依賴於該特定對象所承擔的責任,咱們就須要獲得該特定對象。那該類就是一個單例模式的實現了。 
    
二 如何開發線程安全的Servlet 
    1,變量的線程安全:這裏的變量指字段和共享數據(如表單參數值)。

      a,將 參數變量本地化。多線程並不共享局部變量.因此咱們要儘量的在servlet中使用局部變量。 
      例如:String user = ""; 
         user = request.getParameter("user");

      b,使用同步塊Synchronized,防止可能異步調用的代碼塊。這意味着線程須要排隊處理。 
在使用同步塊的時候要儘量的縮小同步代碼的範圍,不要直接在sevice方法和響應方法上使用同步,這樣會嚴重影響性能。

   2,屬性的線程安全:ServletContext,HttpSession,ServletRequest對象中屬性 
     ServletContext:(線程是不安全的) 
    ServletContext是能夠多線程同時讀/寫屬性的,線程是不安全的。要對屬性的讀寫進行同步處理或者進行深度Clone()。 
     因此在Servlet上下文中儘量少許保存會被修改(寫)的數據,能夠採起其餘方式在多個Servlet中共享,比方咱們可使用單例模式來處理共享數據。 
     HttpSession:(線程是不安全的) 
     HttpSession對象在用戶會話期間存在,只能在處理屬於同一個Session的請求的線程中被訪問,所以Session對象的屬性訪問理論上是線程安全的。 
     當用戶打開多個同屬於一個進程的瀏覽器窗口,在這些窗口的訪問屬於同一個Session,會出現屢次請求,須要多個工做線程來處理請求,可能形成同時多線程讀寫屬性。 
     這時咱們須要對屬性的讀寫進行同步處理:使用同步塊Synchronized和使用讀/寫器來解決。

     ServletRequest:(線程是安全的) 
     對於每個請求,由一個工做線程來執行,都會建立有一個新的ServletRequest對象,因此ServletRequest對象只能在一個線程中被訪問。ServletRequest是線程安全的。 
    注意:ServletRequest對象在service方法的範圍內是有效的,不要試圖在service方法結束後仍然保存請求對象的引用。

3,使用同步的集合類: 
    使用Vector代替ArrayList,使用Hashtable代替HashMap。

4,不要在Servlet中建立本身的線程來完成某個功能。 
    Servlet自己就是多線程的,在Servlet中再建立線程,將致使執行狀況複雜化,出現多線程安全問題。

5,在多個servlet中對外部對象(比方文件)進行修改操做必定要加鎖,作到互斥的訪問。 
   
6,javax.servlet.SingleThreadModel接口是一個標識接口,若是一個Servlet實現了這個接口,那Servlet容器將保證在一個時刻僅有一個線程能夠在給定的servlet實例的service方法中執行。將其餘全部請求進行排隊。 
   服務器可使用多個實例來處理請求,代替單個實例的請求排隊帶來的效益問題。服務器建立一個Servlet類的多個Servlet實例組成的實例池,對於每一個請求分配Servlet實例進行響應處理,以後放回到實例池中等待下此請求。這樣就形成併發訪問的問題。 
此時,局部變量(字段)也是安全的,但對於全局變量和共享數據是不安全的,須要進行同步處理。而對於這樣多實例的狀況SingleThreadModel接口並不能解決併發訪問問題。

SingleThreadModel接口在servlet規範中已經被廢棄了。

相關文章
相關標籤/搜索