標籤:包括 方法 不能 容器 成員方法 持久 htm 靜態變數 calendar
一、Spring單例模式及安全執行緒
Spring架構中的Bean,或者說組件,擷取執行個體的時候都是預設單例模式,這是在多線程開發的時候需要尤其注意的地方。
單例模式的意思是只有一個執行個體,例如在Spring容器中某一個類只有一個執行個體,而且自行執行個體化後並項整個系統提供這個執行個體,這個類稱為單例類。
當多個使用者同時請求一個服務時,容器會給每一個請求分配一個線程,這時多個線程會並發執行該請求對應的商務邏輯(成員方法),此時就要注意了,如果該處理邏輯中有對單例狀態的修改(體現為該單例的成員屬性),則必須考慮線程同步問題。
同步機制的比較:
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範圍二、安全執行緒案例
SimpleDateFormat(下面簡稱sdf)類內部有一個Calendar對象引用,它用來儲存和這個sdf相關的日期資訊,例如sdf.parse(dateStr), sdf.format(date) 諸如此類的方法參數傳入的日期相關String, Date等等, 都是交友Calendar引用來儲存的.這樣就會導致一個問題,如果你的sdf是個static的, 那麼多個thread 之間就會共用這個sdf, 同時也是共用這個Calendar引用,非安全執行緒的。
這個問題背後隱藏著一個更為重要的問題--無狀態:無狀態方法的好處之一,就是它在各種環境下,都可以安全的調用。衡量一個方法是否是有狀態的,就看它是否改動了其它的東西,比如全域變數,比如執行個體的欄位。format方法在運行過程中改動了SimpleDateFormat的calendar欄位,所以,它是有狀態的。
這也同時提醒我們在開發和設計系統的時候注意下一下三點:
1.自己寫公用類的時候,要對多線程調用情況下的後果在注釋裡進行明確說明
2.對線程環境下,對每一個共用的可變變數都要注意其執行緒安全性
3.我們的類和方法在做設計的時候,要盡量設計成無狀態的
解決方案:
1.使用synchronized關鍵字進行資料同步,或者使用ThreadLocal保證安全執行緒
2.不適用JDK內建的時間格式化類,使用其他類庫的
- 使用Apache commons裡的FastDateFormat,宣城是既快有安全執行緒的SimpleDateFormat,可惜他只能對日期進行format,不能對日期串進行解析
- 使用Joda-Time類庫來處理時間相關問題,該種對時間的處理方式比較完美,建議使用。
原文連結:https://www.cnblogs.com/redcool/p/6398760.html
【轉】Spring Bean單例與安全執行緒