spring單例在高併發下可能出現的錯誤

spring單例在高併發下可能出現的錯誤

 

spring單例在高併發下可能出現的錯誤: 首先,只有當注入的對象是無狀態的冪等的纔可以保證執行前後不被修改,否則執行一次之後單例對象就會發生改變,在下次執行有肯能造成結果不一樣,當在高併發的情況下就會出現,這個線程剛使用單例對象進行屬性設置,還未使用的情況下,另一個進程已經將單利對象的數據進行修改屬性完成,則遠來線程獲取到的單例就是一個髒對象不可使用。 當單例對象中含有變化的變量數據,則就不可以使用對象注入的形式注入,那怎麼辦? 1、在單例對象中進行new對象,這屬於線程自己的內存進行存放數據,其他線程無法使用 2、在單例對象中注入了可變對象,則在使用的時候進行copy淺拷貝後使用拷貝後的對象不實用單例對象 3、通過new Callable()輕量級的創建局部變量可以使用上下文變量,屬於線程自己的變量 總結:在高併發情況下,單利對象的數據不可以在一個線程使用過,另一個線程調用時單例對象的數據發生改變。 其實單例對象相當於全局變量,線程執行時需要修改數據,再高併發的情況下就會出現當前線程獲取到的單例對象數據是髒數據。

 

Spring單例與線程安全小結

一、Spring單例模式與線程安全

 

 
Spring框架裏的bean,或者說組件,獲取實例的時候都是默認的單例模式,這是在多線程開發的時候要尤其注意的地方。

 

單例模式的意思就是隻有一個實例。單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。這個類稱爲單例類。
當多用戶同時請求一個服務時,容器會給每一個請求分配一個線程,這是多個線程會併發執行該請求多對應的業務邏輯(成員方法),此時就要注意了,如果該處理邏輯中有對該單列狀態的修改(體現爲該單列的成員屬性),則必須考慮線程同步問題
同步機制的比較  ThreadLocal和線程同步機制相比有什麼優勢呢?ThreadLocal和線程同步機制都是爲了解決多線程中相同變量的訪問衝突問題。 
 
  在同步機制中,通過對象的鎖機制保證同一時間只有一個線程訪問變量。這時該變量是多個線程共享的,使用同步機制要求程序慎密地分析什麼時候對變量進行讀寫,什麼時候需要鎖定某個對象,什麼時候釋放對象鎖等繁雜的問題,程序設計和編寫難度相對較大。 
 
  而ThreadLocal則從另一個角度來解決多線程的併發訪問。ThreadLocal會爲每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的訪問衝突。因爲每一個線程都擁有自己的變量副本,從而也就沒有必要對該變量進行同步了。ThreadLocal提供了線程安全的共享對象,在編寫多線程代碼時,可以把不安全的變量封裝進ThreadLocal。 
 
  由於ThreadLocal中可以持有任何類型的對象,低版本JDK所提供的get()返回的是Object對象,需要強制類型轉換。但JDK 5.0通過泛型很好的解決了這個問題,在一定程度地簡化ThreadLocal的使用
 概括起來說,對於多線程資源共享的問題,同步機制採用了“以時間換空間”的方式,而ThreadLocal採用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而後者爲每一個線程都提供了一份變量,因此可以同時訪問而互不影響。 
 
  Spring使用ThreadLocal解決線程安全問題 
 
  我們知道在一般情況下,只有無狀態的Bean纔可以在多線程環境下共享,在Spring中,絕大部分Bean都可以聲明爲singleton作用域。就是因爲Spring對一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非線程安全狀態採用ThreadLocal進行處理,讓它們也成爲線程安全的狀態,因爲有狀態的Bean就可以在多線程中共享了。 
 
  一般的Web應用劃分爲展現層、服務層和持久層三個層次,在不同的層中編寫對應的邏輯,下層通過接口向上層開放功能調用。在一般情況下,從接收請求到返回響應所經過的所有程序調用都同屬於一個線程
ThreadLocal是解決線程安全問題一個很好的思路,它通過爲每個線程提供一個獨立的變量副本解決了變量併發訪問的衝突問題。在很多情況下,ThreadLocal比直接使用synchronized同步機制解決線程安全問題更簡單,更方便,且結果程序擁有更高的併發性。 
如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。 或者說:一個類或者程序所提供的接口對於線程來說是原子操作或者多個線程之間的切換不會導致該接口的執行結果存在二義性,也就是說我們不用考慮同步的問題。  線程安全問題都是由全局變量及靜態變量引起的。  
若每個線程中對全局變量、靜態變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線程同步,否則就可能影響線程安全。
1) 常量始終是線程安全的,因爲只存在讀操作。 
2)每次調用方法前都新建一個實例是線程安全的,因爲不會訪問共享的資源。
3)局部變量是線程安全的。因爲每執行一個方法,都會在獨立的空間創建局部變量,它不是共享的資源。局部變量包括方法的參數變量和方法內變量。
有狀態就是有數據存儲功能。有狀態對象(Stateful Bean),就是有實例變量的對象  ,可以保存數據,是非線程安全的。在不同方法調用間不保留任何狀態。
無狀態就是一次操作,不能保存數據。無狀態對象(Stateless Bean),就是沒有實例變量的對象  .不能保存數據,是不變類,是線程安全的。
有狀態對象:
無狀態的Bean適合用不變模式,技術就是單例模式,這樣可以共享實例,提高性能。有狀態的Bean,多線程環境下不安全,那麼適合用Prototype原型模式。Prototype: 每次對bean的請求都會創建一個新的bean實例。
Struts2默認的實現是Prototype模式。也就是每個請求都新生成一個Action實例,所以不存在線程安全問題。需要注意的是,如果由Spring管理action的生命週期, scope要配成prototype作用域

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章