1. 程式人生 > >Java Web併發訪問的執行緒安全問題

Java Web併發訪問的執行緒安全問題

Java Web併發訪問的執行緒安全問題

2018年05月12日 02:02:52 菜鳥級的IT之路 閱讀數:68更多

個人分類: JAVA~JavaWeb

一、Servlet的執行緒安全問題

Java web伺服器下,每個Servlet只有一個例項(即單例模式),導致有多個Http請求發給一個Servlet例項,每個請求是一個執行緒。如果Servlet有類變數或例項變數,那麼該變數就變成了共享資源,當多個執行緒訪問操作該變數時,就有可能存在安全隱患。例如,當一個Http請求在訪問該變數的時候,另一個Http請求可能在修改它的值。

解決方法:在Servlet中不要定義類變數或例項變數,如果非定義不可,就需要為操作該變數的所有方法加synchronized修飾符,進行同步控制。

注意: 
1. 在Servlet中可以定義常量 
2. 如果定義的變數只用查詢操作,可以不加同步控制 
3. 被Servlet呼叫的類中(如值物件、領域模型類)中是否可以安全的使用例項變數?如果你在每次方法呼叫時新建一個物件,再呼叫它們的方法,則不存在同步問題。因為它們不是多個執行緒共享的資源,只有共享的資源才需要同步,而Servlet的例項對於多個執行緒是共享的。

二、Struts併發訪問的執行緒安全問題

同Servlet一樣,Struts中的Action也是單例的,所以多Http請求訪問時,也存線上程安全問題。

解決方法:同Servlet

三、Spring併發訪問的執行緒安全性問題

和Struts一樣,Spring的Controller預設是Singleton的(非執行緒安全的),這意味著每個request過來,系統都會用原有的instance去處理,這樣導致了兩個結果:一是我們不用每次建立Controller,二是減少了物件建立和垃圾收集的時間;由於只有一個Controller的instance,當多個執行緒呼叫它的時候,它裡面的instance變數就不是執行緒安全的了,會發生竄資料的問題。

當然大多數情況下,我們根本不需要考慮執行緒安全的問題,除非在Controller中聲明瞭例項變數。因此,我們在使用spring mvc 的contrller時,應避免在controller中定義類變數或例項變數。 

如:

 
  1. public class Controller extends AbstractCommandController {

  2. ......

  3. protected Company company;

  4. protected ModelAndView handle(HttpServletRequest request,HttpServletResponse response,

  5. Object command,BindException errors) throws Exception {

  6. company = ................;

  7. }

  8. }

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

有幾種解決方法: 
1、在Controller中使用ThreadLocal變數 
2、在spring配置檔案Controller中宣告 scope=”prototype”,每次都建立新的controller。

注意:在Spring mvc中,如果dao、service也是單例的,要避免在Dao和Service類中建立類變數或例項變數。

四、SpringMVC多執行緒環境中如何保證物件的安全性?

http://linapex.blog.163.com/blog/static/189237516201381493441799/

總結:多執行緒環境下如果訪問單例物件,當物件內部有類變數或例項變數時,就可能存在安全性問題。 
解決方法: 
1.對操作共享變數的所用方法進行同步控制; 
2.同步共享變數,例如Collections.synchronizedMap()可以同步共享的Map。 

3.使用同步物件,例如ConcurrentMap、AtomicInteger等物件都是執行緒安全的,使用AtomicInteger可以統計系統的併發量。

==========================================================================================

 

在web開發中,要關注由於併發訪問所導致的對某一同一個值的修改,否則資訊會造成洩漏servlet是在多執行緒環境下的。即可能有多個請求發給一個servelt例項,每個請求是一個執行緒。
struts下的action也類似,同樣在多執行緒環境下。可以參考struts user guide: http://struts.apache.org/struts-action/userGuide/building_controller.html 中的Action Class Design Guidelines一節:  Write code for a multi-threaded environment - Our controller servlet creates only one instance of your Action class, and uses this one instance to service all requests. Thus, you need to write thread-safe Action classes. Follow the same guidelines you would use to write thread-safe Servlets. 
    譯:為多執行緒環境編寫程式碼。我們的controller servlet指揮建立你的Action 類的一個例項,用此例項來服務所有的請求。因此,你必須編寫執行緒安全的Action類。遵循與寫執行緒安全的servlet同樣的方針。
  
1.什麼是執行緒安全的程式碼
    在多執行緒環境下能正確執行的程式碼就是執行緒安全的。
    安全的意思是能正確執行,否則後果是程式執行錯誤,可能出現各種異常情況。

2.如何編寫執行緒安全的程式碼
    很多書籍裡都詳細講解了如何這方面的問題,他們主要講解的是如何同步執行緒對共享資源的使用的問題。主要是對synchronized關鍵字的各種用法,以及鎖的概念。
    Java1.5中也提供瞭如讀寫鎖這類的工具類。這些都需要較高的技巧,而且相對難於除錯。
  
    但是,執行緒同步是不得以的方法,是比較複雜的,而且會帶來效能的損失。等效的程式碼中,不需要同步在編寫容易度和效能上會更好些。
  我這裡強調的是什麼程式碼是始終為執行緒安全的、是不需要同步的。如下:
  1)常量始終是執行緒安全的,因為只存在讀操作。
  2)對構造器的訪問(new 操作)是執行緒安全的,因為每次都新建一個例項,不會訪問共享的資源。
  3)最重要的是:區域性變數是執行緒安全的。因為每執行一個方法,都會在獨立的空間建立區域性變數,它不是共享的資源。區域性變數包括方法的引數變數。
 struts user guide裡有:
    Only Use Local Variables - The most important principle that aids in thread-safe coding is to use only local variables, not instance variables , in your Action class.
   譯:只使用用區域性變數。---編寫執行緒安全的程式碼最重要的原則就是,在Action類中只使用區域性變數,不使用例項變數。

 
總結:
     在Java的Web伺服器環境下開發,要注意執行緒安全的問題。最簡單的實現方式就是在Servlet和Struts Action裡不要使用類變數、例項變數,但可以使用類常量和例項常量。
如果有這些變數,可以將它們轉換為方法的引數傳入,以消除它們。
     注意一個容易混淆的地方:被Servlet或Action呼叫的類中(如值物件、領域模型類)中是否可以安全的使用例項變數?如果你在每次方法呼叫時
新建一個物件,再呼叫它們的方法,則不存在同步問題---因為它們不是多個執行緒共享的資源,只有共享的資源才需要同步---而Servlet和Action的例項對於多個執行緒是共享的。
    換句話說,Servlet和Action的例項會被多個執行緒同時呼叫,而過了這一層,如果在你自己的程式碼中沒有另外啟動執行緒,且每次呼叫後續業務物件時都是先新建一個例項再呼叫,則都是執行緒安全的。