springMVC一個Controller處理所有用戶請求的併發問題

springMVC一個Controller處理所有用戶請求的併發問題

有狀態和無狀態的對象基本概念: 
有狀態對象(Stateful Bean),就是有實例變量的對象 ,可以保存數據,是非線程安全的。一般是prototype scope。
無狀態對象(Stateless Bean),就是沒有實例變量的對象,不能保存數據,是不變類,是線程安全的。一般是singleton scope。

如Struts2中的Action,假如內部有實例變量User,當調用新增用戶方法時,user是用來保存數據,那麼此action是有狀態對象。多個線程同時訪問此action時 會造成user變量的不一致。所以action的scope要設計成prototype,或者,User類放到threadLocal裏來保持多個線程不會造成User變量的亂串(此種場景沒必要放到threadLocal內)。

而Service內部一般只有dao實例變量 如userDao, 因爲userDao是無狀態的對象(內部無實例變量且不能保存數據),所以service也是無狀態的對象。

public   class  XxxAction{

   // 由於多線程環境下,user是引用對象,是非線程安全的  

  public  User user;

  ......

}

public   class  XxxService {  

  // 雖然有billDao屬性,但billDao是沒有狀態信息的,是Stateless Bean.   

  BillDao billDao;  

  ......

}

對於那些會以多線程運行的單例類

局部變量不會受多線程影響,
成員變量會受到多線程影響。

多個線程調用同一個對象的同一個方法: 
如果方法裏無局部變量,那麼不受任何影響;
如果方法裏有局部變量,只有讀操作,不受影響;存在寫操作,考慮多線程影響值;

例如Web應用中的Servlet,每個方法中對局部變量的操作都是在線程自己獨立的內存區域內完成的,所以是線程安全的。 
對於成員變量的操作,可以使用ThreadLocal來保證線程安全。 
 

springMVC中,一般Controller、service、DAO層的scope均是singleton;

每個請求都是單獨的線程,即使同時訪問同一個Controller對象,因爲並沒有修改Controller對象,相當於針對Controller對象而言,只是讀操作,沒有寫操作,不需要做同步處理。

 

Service層、Dao層用默認singleton就行,雖然Service類也有dao這樣的屬性,但dao這些類都是沒有狀態信息的,也就是 相當於不變(immutable)類,所以不影響。

Struts2中的Action因爲會有User、BizEntity這樣的實例對象,是有狀態信息 的,在多線程環境下是不安全的,所以Struts2默認的實現是Prototype模式。在Spring中,Struts2的Action中scope 要配成prototype作用域。

 

 

Spring併發訪問的線程安全性問題  

由於Spring MVC默認是Singleton的,所以會產生一個潛在的安全隱患。根本核心是instance變量保持狀態的問題。這意味着每個request過來,系統都會用原有的instance去處理,這樣導致了兩個結果:
一是我們不用每次創建Controller,
二是減少了對象創建和垃圾收集的時間;
由於只有一個Controller的instance,當多個線程同時調用它的時候,它裏面的instance變量就不是線程安全的了,會發生竄數據的問題。
當然大多數情況下,我們根本不需要考慮線程安全的問題,比如dao,service等,除非在bean中聲明瞭實例變量。因此,我們在使用spring mvc 的contrller時,應避免在controller中定義實例變量。 
如:

publicclassControllerextendsAbstractCommandController{
......
protectedModelAndView handle(HttpServletRequest request,HttpServletResponse response,
Object command,BindException errors)throwsException{
company =................;
}
protectedCompany company;
}

 

在這裏有聲明一個變量company,這裏就存在併發線程安全的問題。
如果控制器是使用單例形式,且controller中有一個私有的變量a,所有請求到同一個controller時,使用的a變量是共用的,即若是某個請求中修改了這個變量a,則,在別的請求中能夠讀到這個修改的內容。。

有幾種解決方法:
1、在控制器中不使用實例變量
2、將控制器的作用域從單例改爲原型,即在spring配置文件Controller中聲明 scope="prototype",每次都創建新的controller
3、在Controller中使用ThreadLocal變量

這幾種做法有好有壞,第一種,需要開發人員擁有較高的編程水平與思想意識,在編碼過程中力求避免出現這種BUG,而第二種則是容器自動的對每個請求產生一個實例,由JVM進行垃圾回收,因此做到了線程安全。
使用第一種方式的好處是實例對象只有一個,所有的請求都調用該實例對象,速度和性能上要優於第二種,不好的地方,就是需要程序員自己去控制實例變量的狀態保持問題。第二種由於每次請求都創建一個實例,所以會消耗較多的內存空間。
所以在使用spring開發web 時要注意,默認Controller、Dao、Service都是單例的

發佈了49 篇原創文章 · 獲贊 59 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章