Spring中的單例模式使用
1 spring單例 V.S 設計模式的單例
設計模式單例,在整個應用中只有一個實例
spring單例,在一個IoC容器中只有一個實例
但spring中的單例也不影響應用并發訪問。大多數時候客戶端都在訪問我們應用中的業務對象,為減少并發控制,不應該在業務對象中設置那些容易造成出錯的成員變量。
成員變量的解決方式
方法的參數,局部變量(相當于new)
threadlocal、設置bean scope=prototype
Spring Bean Scope 有狀態的Bean 無狀態的Bean遇到的情況
Spring并發問題
一般無狀態的Bean才可在多線程環境下共享,Spring Bean默認為singleton作用域。
那有狀態bean呢?Spring對一些如
RequestContextHolder
TransactionSynchronizationManager
LocaleContextHolder
…
非線程安全狀態Bean采用ThreadLocal,讓它們也成為線程安全的狀態。
如用有狀態bean,也可使用prototype模式,每次在注入時,就重新創建一個bean,在多線程中互不影響。
Eic-server所有的業務對象中的成員變量如:
Dao中的xxxDao
controller中的xxxService
都會被多個線程共享,那這些對象不會出現同步問題嗎?比如造成DB插入,更新異常?
實體bean,從客戶端傳遞到后臺controller=》service=>Dao流程中,他們這些對象都是單例的,那這些單例對象在處理我們的傳遞到后臺的實體bean不會出問題嗎?(實體bean在多線程中的解決方案)
因為實體bean不是單例的,他們并沒有交給Spring管理!每次我們都手動的New出來的,如BigObject bo = new BigObject(),所以即使是那些處理我們提交數據的業務處理類是被多線程共享,但他們處理的數據并不共享,數據是每個線程都有自己的一份,所以在數據方面不會出現線程安全問題。
對實體bean一般通過方法參數的的形式傳遞(參數是局部變量),所以多線程間不會有影響
有的地方對有狀態的bean直接使用prototype
對使用bean的地方,可通過new創建
但那些:
在Dao中的xxxDao
controller中的xxxService
這些對象都是單例,那就不會出現線程同步問題。這些對象雖被多線程并發訪問,可我們訪問的是他們里面的方法,而這些類里面通常不會有成員變量。所以出問題的是系統里面的業務對象,務必注意這些業務對象里,千萬不能有獨立的成員變量,否則會出錯。
所以我們在應用中的業務對象如下
controller中的成員變量List和paperService:
service里的成員變量ibatisEntityDao:
雖然這個應用有成員變量,但不會出現線程安全問題:
controller里的成員變量private TestPaperService papersService 之所以會成為成員變量,我們的目的是注入,將其實例化進而訪問里面的方法
private static final int List = 0;是final的不會被改變
service里面的private IbatisEntityDao ibatisEntityDao;是框架本身的,線程同步問題已解決
spring無狀態的支持
Spring框架對單例的支持是采用單例注冊表。
spring有狀態的支持
spring如何實現那些個有狀態bean,如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder]的線程安全,即使用ThreadLocal實現的
Spring中ThreadLocal的認識
淺談Spring聲明式事務管理ThreadLocal和JDKProxy
ThreadLocal
當使用ThreadLocal維護變量(僅是變量,因為線程同步問題就是成員變量的互斥訪問出問題)時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每個線程都可獨立改變自己的副本,而不會影響其它線程所對應副本。
從線程角度看,就好像每一個線程都完全擁有該變量,這其實就將共享變相為人手一份。雖使用ThreadLocal帶來更多內存開銷,但這點開銷還微不足道。因為保存在ThreadLocal中的對象,通常較小。
initialValue(),protected的方法,為子類重寫而實現。該方法返回當前線程在該線程局部變量的初始值,是個延遲調用方法,在一個線程第1次調用get()或set(Object)時才執行且僅執行1次。
ThreadLocal使用
要給線程初始化一個特殊值時,需要自己實現ThreadLocal的子類并重寫該方法,通常使用一個內部匿名類對ThreadLocal進行子類化,EasyDBO中創建jdbc連接上下文就是這樣做的:
簡單實現版本:
ThreadLocal V.S synchronized
為保證多個線程對共享變量的安全訪問,通常會使用synchronized保證同時只有一個線程對共享變量進行操作。但有些情況下,synchronized不能保證多線程對共享變量的正確讀寫。
例如類有個類變量,該類變量會被多個類方法讀寫,當多線程操作該類的實例對象時,若線程對類變量有讀取、寫入操作就會發生類變量讀寫錯誤,即便是在類方法前加上synchronized也無效,因為同一個線程在兩次調用方法之間時鎖是被釋放的,這時其它線程可訪問對象的類方法,讀取或修改類變量。這種情況下可以將類變量放到ThreadLocal中,使變量在每個線程中都有獨立拷貝,不會出現一個線程讀取變量時而被另一個線程修改的現象。
多線程訪問對于類變量和ThreadLocal變量的影響,QuerySvc分別設置:
類變量sql
ThreadLocal變量
使用時先創建QuerySvc的一個實例對象,然后產生多個線程,分別設置不同sql實例對象,再調用execute,讀取sql的值,看是否是set方法中寫入的值。這類似web應用中多個請求線程攜帶不同查詢條件對一個servlet實例的訪問,然后servlet調用業務對象,并傳入不同查詢條件,最后要保證每個請求得到的結果是對應的查詢條件的結果。
使用QuerySvc的工作線程如下:
運行線程:
先創建一個QuerySvc實例對象,然后創建若干線程來調用QuerySvc的set和execute方法,每個線程傳入的sql都不一樣,sql變量中值不能保證在execute中值和set設置的值一樣,在web應用中就表現為一個用戶查詢的結果不是自己的查詢條件返回的結果,而是另一個用戶查詢條件的結果。
而ThreadLocal中的值總是和set中設置的值一樣,這樣通過使用ThreadLocal獲得了線程安全性。
小結
若一個對象要被多個線程訪問,而該對象存在類變量被不同類方法讀寫,為獲得線程安全,可以用ThreadLocal替代類變量。
ThreadLocal和線程同步機制相比有什么優勢呢?
ThreadLocal和線程同步機制都是為了解決多線程中相同變量的訪問沖突問題。
同步機制中,通過對象的鎖機制保證同一時間只有一個線程訪問變量。這時該變量是多個線程共享的,使用同步機制要分析:
什么時候對變量進行讀寫
什么時候需要鎖定某個對象
什么時候釋放對象鎖等繁雜的問題
而ThreadLocal為每個線程提供一個獨立變量副本,隔離多線程對數據的訪問沖突。ThreadLocal采用“以空間換時間”。
Spring使用ThreadLocal解決線程安全問題
一般只有無狀態Bean才能在多線程下共享,在Spring中,絕大部分Bean都可以聲明為singleton作用域。因為Spring對一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非線程安全狀態采用ThreadLocal進行處理,讓它們也成為線程安全的狀態,有狀態Bean就能在多線程中共享了。
一般Web應用劃分為展現層、服務層和持久層三個層次,從接收請求到返回響應所經過的所有程序調用都同屬于一個線程。這就能根據需要,將一些非線程安全的變量以ThreadLocal存放,在同一次請求響應的調用線程中,所有關聯的對象引用到的都是同一個變量。
ThreadLocal是解決線程安全問題一個很好的思路,它通過為每個線程提供一個獨立的變量副本解決了變量并發訪問的沖突問題。在很多情況下,ThreadLocal比直接使用synchronized同步機制解決線程安全問題更簡單,更方便,且結果程序擁有更高的并發性。
在spring管理【ThreadLocal管理的類變量,他也僅僅是在管理變量而已】。
Spring的單例模式底層實現
Spring
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。