文章出處

struts2中action是多例的,即一個session產生一個action
如果是單例的話,若出現兩個用戶都修改一個對象的屬性值,則會因為用戶修改時間不同,兩個用戶訪問得到的

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

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

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

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

題了
struts 2的Action是多實例的并非單例,也就是每次請求產生一個Action的對象。原因是:struts 2的Action

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

數據在多線程的環境下就會相互影響,例如造成別人填寫的數據被你看到了。所以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規范中已經被廢棄了。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()