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中定義類變數或例項變數。
如:
-
public class Controller extends AbstractCommandController {
-
......
-
protected Company company;
-
protected ModelAndView handle(HttpServletRequest request,HttpServletResponse response,
-
Object command,BindException errors) throws Exception {
-
company = ................;
-
}
-
}
在這裡有宣告一個變數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的例項會被多個執行緒同時呼叫,而過了這一層,如果在你自己的程式碼中沒有另外啟動執行緒,且每次呼叫後續業務物件時都是先新建一個例項再呼叫,則都是執行緒安全的。