Java中Spring的單例模式使用
1.spring單例 V.S 設(shè)計(jì)模式的單例
- 設(shè)計(jì)模式單例,在整個(gè)應(yīng)用中只有一個(gè)實(shí)例
- spring單例,在一個(gè)IoC容器中只有一個(gè)實(shí)例
但spring
中的單例也不影響應(yīng)用并發(fā)訪問。大多數(shù)時(shí)候客戶端都在訪問我們應(yīng)用中的業(yè)務(wù)對象,為減少并發(fā)控制,不應(yīng)該在業(yè)務(wù)對象中設(shè)置那些容易造成出錯(cuò)的成員變量。
2.成員變量的解決方式
- 方法的參數(shù),局部變量(相當(dāng)于new)
threadlocal
、設(shè)置bean scope=prototype
Spring Bean Scope 有狀態(tài)的Bean與無狀態(tài)的Bean
3.Spring并發(fā)問題
一般無狀態(tài)的Bean才可在多線程環(huán)境下共享,Spring Bean
默認(rèn)為singleton
作用域。
那有狀態(tài)bean呢?Spring對一些如
- RequestContextHolder
- TransactionSynchronizationManager
- LocaleContextHolder
非線程安全狀態(tài)Bean采用ThreadLocal
,讓它們也成為線程安全的狀態(tài)。
如用有狀態(tài)bean,也可使用prototype模式,每次在注入時(shí),就重新創(chuàng)建一個(gè)bean,在多線程中互不影響。
Eic-server所有的業(yè)務(wù)對象中的成員變量如:
- Dao中的xxxDao
- controller中的xxxService
都會被多個(gè)線程共享,那這些對象不會出現(xiàn)同步問題嗎?比如造成DB插入,更新異常?
實(shí)體bean,從客戶端傳遞到后臺controller=》service=>Dao流程中,他們這些對象都是單例的,那這些單例對象在處理我們的傳遞到后臺的實(shí)體bean不會出問題嗎?(實(shí)體bean在多線程中的解決方案)
因?yàn)閷?shí)體bean不是單例的,他們并沒有交給Spring管理!每次我們都手動的New出來的,如BigObject bo = new BigObject(),所以即使是那些處理我們提交數(shù)據(jù)的業(yè)務(wù)處理類是被多線程共享,但他們處理的數(shù)據(jù)并不共享,數(shù)據(jù)是每個(gè)線程都有自己的一份,所以在數(shù)據(jù)方面不會出現(xiàn)線程安全問題。
4.對實(shí)體bean在多線程中的處理
- 對實(shí)體bean一般通過方法參數(shù)的的形式傳遞(參數(shù)是局部變量),所以多線程間不會有影響
- 有的地方對有狀態(tài)的bean直接使用
prototype
- 對使用bean的地方,可通過new創(chuàng)建
- 在Dao中的xxxDao
- controller中的xxxService
這些對象都是單例,那就不會出現(xiàn)線程同步問題。這些對象雖被多線程并發(fā)訪問,可我們訪問的是他們里面的方法,而這些類里面通常不會有成員變量。所以出問題的是系統(tǒng)里面的業(yè)務(wù)對象,務(wù)必注意這些業(yè)務(wù)對象里,千萬不能有獨(dú)立的成員變量,否則會出錯(cuò)。
所以我們在應(yīng)用中的業(yè)務(wù)對象如下:
controller中的成員變量List和paperService:
service里的成員變量ibatisEntityDao:
雖然這個(gè)應(yīng)用有成員變量,但不會出現(xiàn)線程安全問題:
- controller里的成員變量
private TestPaperService papersService
之所以會成為成員變量,我們的目的是注入,將其實(shí)例化進(jìn)而訪問里面的方法 private static final int List = 0
;是final的不會被改變- service里面的
private IbatisEntityDao ibatisEntityDao
;是框架本身的,線程同步問題已解決
5.spring無狀態(tài)的支持
Spring框架對單例的支持是采用單例注冊表。
6.spring有狀態(tài)的支持
spring如何實(shí)現(xiàn)那些個(gè)有狀態(tài)bean,如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder]的線程安全,即使用ThreadLocal實(shí)現(xiàn)的
7.ThreadLocal
當(dāng)使用ThreadLocal
維護(hù)變量(僅是變量,因?yàn)榫€程同步問題就是成員變量的互斥訪問出問題)時(shí),ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,所以每個(gè)線程都可獨(dú)立改變自己的副本,而不會影響其它線程所對應(yīng)副本。
從線程角度看,就好像每一個(gè)線程都完全擁有該變量,這其實(shí)就將共享變相為人手一份。雖使用ThreadLocal帶來更多內(nèi)存開銷,但這點(diǎn)開銷還微不足道。因?yàn)楸4嬖赥hreadLocal中的對象,通常較小。
initialValue()
,protected的方法,為子類重寫而實(shí)現(xiàn)。該方法返回當(dāng)前線程在該線程局部變量的初始值,是個(gè)延遲調(diào)用方法,在一個(gè)線程第1次調(diào)用get()或set(Object)時(shí)才執(zhí)行且僅執(zhí)行1次。
8.ThreadLocal使用
要給線程初始化一個(gè)特殊值時(shí),需要自己實(shí)現(xiàn)ThreadLocal的子類并重寫該方法,通常使用一個(gè)內(nèi)部匿名類對ThreadLocal進(jìn)行子類化,
EasyDBO中創(chuàng)建jdbc連接上下文就是這樣做的:
簡單實(shí)現(xiàn)版本:
9.ThreadLocal V.S synchronized
為保證多個(gè)線程對共享變量的安全訪問,通常會使用synchronized
保證同時(shí)只有一個(gè)線程對共享變量進(jìn)行操作。但有些情況下,synchronized
不能保證多線程對共享變量的正確讀寫。
例如類有個(gè)類變量,該類變量會被多個(gè)類方法讀寫,當(dāng)多線程操作該類的實(shí)例對象時(shí),若線程對類變量有讀取、寫入操作就會發(fā)生類變量讀寫錯(cuò)誤,即便是在類方法前加上synchronized也無效,因?yàn)橥粋€(gè)線程在兩次調(diào)用方法之間時(shí)鎖是被釋放的,這時(shí)其它線程可訪問對象的類方法,讀取或修改類變量。這種情況下可以將類變量放到ThreadLocal
中,使變量在每個(gè)線程中都有獨(dú)立拷貝,不會出現(xiàn)一個(gè)線程讀取變量時(shí)而被另一個(gè)線程修改的現(xiàn)象。
多線程訪問對于類變量和ThreadLocal變量的影響,QuerySvc分別設(shè)置:
- 類變量sql
- ThreadLocal變量
使用時(shí)先創(chuàng)建QuerySvc
的一個(gè)實(shí)例對象,然后產(chǎn)生多個(gè)線程,分別設(shè)置不同sql實(shí)例對象,再調(diào)用execute,讀取sql的值,看是否是set方法中寫入的值。這類似web應(yīng)用中多個(gè)請求線程攜帶不同查詢條件對一個(gè)servlet
實(shí)例的訪問,然后servlet調(diào)用業(yè)務(wù)對象,并傳入不同查詢條件,最后要保證每個(gè)請求得到的結(jié)果是對應(yīng)的查詢條件的結(jié)果。
使用QuerySvc的工作線程如下:
運(yùn)行線程:
先創(chuàng)建一個(gè)QuerySvc
實(shí)例對象,然后創(chuàng)建若干線程來調(diào)用QuerySvc
的set和execute方法,每個(gè)線程傳入的sql都不一樣,sql變量中值不能保證在execute中值和set設(shè)置的值一樣,在web應(yīng)用中就表現(xiàn)為一個(gè)用戶查詢的結(jié)果不是自己的查詢條件返回的結(jié)果,而是另一個(gè)用戶查詢條件的結(jié)果。
而ThreadLocal中的值總是和set中設(shè)置的值一樣,這樣通過使用ThreadLocal獲得了線程安全性。
小結(jié):
若一個(gè)對象要被多個(gè)線程訪問,而該對象存在類變量被不同類方法讀寫,為獲得線程安全,可以用ThreadLocal
替代類變量。
ThreadLocal和線程同步機(jī)制相比有什么優(yōu)勢呢?
ThreadLocal和線程同步機(jī)制都是為了解決多線程中相同變量的訪問沖突問題。
同步機(jī)制中,通過對象的鎖機(jī)制保證同一時(shí)間只有一個(gè)線程訪問變量。
這時(shí)該變量是多個(gè)線程共享的,使用同步機(jī)制要分析:
- 什么時(shí)候?qū)ψ兞窟M(jìn)行讀寫
- 什么時(shí)候需要鎖定某個(gè)對象
- 什么時(shí)候釋放對象鎖等繁雜的問題
而ThreadLocal
為每個(gè)線程提供一個(gè)獨(dú)立變量副本,隔離多線程對數(shù)據(jù)的訪問沖突。ThreadLocal采用“以空間換時(shí)間”。
10.Spring使用ThreadLocal解決線程安全問題
一般只有無狀態(tài)Bean才能在多線程下共享,在Spring中,絕大部分Bean都可以聲明為singleton作用域。因?yàn)镾pring對一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非線程安全狀態(tài)采用ThreadLocal進(jìn)行處理,讓它們也成為線程安全的狀態(tài),有狀態(tài)Bean就能在多線程中共享了。
一般Web應(yīng)用劃分為展現(xiàn)層、服務(wù)層和持久層三個(gè)層次,從接收請求到返回響應(yīng)所經(jīng)過的所有程序調(diào)用都同屬于一個(gè)線程。這就能根據(jù)需要,將一些非線程安全的變量以ThreadLocal存放,在同一次請求響應(yīng)的調(diào)用線程中,所有關(guān)聯(lián)的對象引用到的都是同一個(gè)變量。
ThreadLocal是解決線程安全問題一個(gè)很好的思路,它通過為每個(gè)線程提供一個(gè)獨(dú)立的變量副本解決了變量并發(fā)訪問的沖突問題。在很多情況下,ThreadLocal比直接使用synchronized同步機(jī)制解決線程安全問題更簡單,更方便,且結(jié)果程序擁有更高的并發(fā)性。
到此這篇關(guān)于Java中Spring的單例模式使用的文章就介紹到這了,更多相關(guān)Spring的單例模式使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IKAnalyzer結(jié)合Lucene實(shí)現(xiàn)中文分詞(示例講解)
下面小編就為大家?guī)硪黄狪KAnalyzer結(jié)合Lucene實(shí)現(xiàn)中文分詞(示例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10springboot 如何配置多個(gè)jndi數(shù)據(jù)源
這篇文章主要介紹了springboot 如何配置多個(gè)jndi數(shù)據(jù)源的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07java的新特性反射機(jī)制應(yīng)用及操作示例詳解
這篇文章主要為大家介紹了java的新特性反射機(jī)制的操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05解決IDEA項(xiàng)目project包目錄消失的問題
這篇文章主要介紹了解決IDEA項(xiàng)目project包目錄消失的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02SpringBoot + 微信公眾號JSAPI支付功能的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot + 微信公眾號JSAPI支付功能的實(shí)現(xiàn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03springboot2?使用activiti6?idea插件的過程詳解
這篇文章主要介紹了springboot2?使用activiti6?idea插件,本文通過截圖實(shí)例代碼相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03SpringCloud中的Ribbon負(fù)載均衡詳細(xì)解讀
這篇文章主要介紹了SpringCloud中的Ribbon負(fù)載均衡詳細(xì)解讀,當(dāng)系統(tǒng)面臨大量的用戶訪問,負(fù)載過高的時(shí)候,通常會增加服務(wù)器數(shù)量來進(jìn)行橫向擴(kuò)展(集群),多個(gè)服務(wù)器的負(fù)載需要均衡,以免出現(xiàn)服務(wù)器負(fù)載不均衡,部分服務(wù)器負(fù)載較大,部分服務(wù)器負(fù)載較小的情況,需要的朋友可以參考下2023-11-11spring cloud feign實(shí)現(xiàn)遠(yuǎn)程調(diào)用服務(wù)傳輸文件的方法
這篇文章主要介紹了spring cloud feign實(shí)現(xiàn)遠(yuǎn)程調(diào)用服務(wù)傳輸文件的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-09-09