Spring中單例和多例的深入理解
Spring單例和多例的理解
1、什么是單例和多例
單例:所有請求用同一個對象來處理。通過單例模式,可以保證系統(tǒng)中一個類只有一個實例。
多例:每個請求用一個新的對象來處理。
2、Spring中的單例與多例
spring ioc容器的bean都是默認單例的,即spring依賴注入Bean實例默認是單例的。
spring提供了5中scope,分別是singleton,prototype,request,session,global session,常用是前兩種。點此查看官網(wǎng)介紹。
單例bean與多例(原型)bean的區(qū)別:
如果一個bean被聲明為單例的時候,在處理多次請求的時候,在spring容器里只實例化出一個bean,后續(xù)的請求都公用這個對象,這個對象會保存在一個map里面。
當(dāng)有請求來的時候,會先從緩存(map)里查看有沒有,有的話直接使用這個對象,沒有的話才實例化一個新的對象,所以這是個單例的。
但是對于原型(prototype)bean來說,當(dāng)每次請求來的時候,會直接實例化新的bean,沒有緩存以及緩存查詢的過程。
3、單例的優(yōu)勢與劣勢
優(yōu)勢:
由于不會創(chuàng)建新的對象,所以有以下幾個性能上的優(yōu)勢:
- 減少新生成實例的消耗。新生成實例包括兩個方面,第一,spring會通過反射或者cglib來生成bean實例,這都是耗性能的操作。第二,給對象分配內(nèi)存也會涉及負責(zé)算法。
- 減少jvm垃圾回收。由于不會給每個請求都生成bean實例,所以回收的對象就少了。
- 可以快速獲取到bean。因為單例獲取bean操作,除了第一次生成之外,其余都是從緩存里獲取的,所以很快。
劣勢:
一個很大的劣勢是它不能做到線程安全。由于所有請求都共享一個bean實例,那么如果這個bean是一個有狀態(tài)的bean的話,在并發(fā)場景下就有可能出現(xiàn)問題。
4、spring單例模式與線程安全:
當(dāng)多用戶同時請求一個服務(wù)時,容器會給每一個請求分配一個線程,這時多個線程會并發(fā)執(zhí)行該請求所對應(yīng)的業(yè)務(wù)邏輯(成員方法),此時就要注意了,如果該處理邏輯中有對該單例狀態(tài)的修改(體現(xiàn)為該單例的成員屬性),則必須考慮線程同步問題(此時該狀態(tài)就是一個臨界資源(共享數(shù)據(jù)),如果多個線程同時操作(修改)這個臨界資源就會誘發(fā)線程安全問題)。
線程安全:如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行的結(jié)果和單線程運行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,就是線程安全的。或者說:一個類或者程序所提供的接口對于線程來說是原子操作,或者多線程之間的切換不會導(dǎo)致該接口的執(zhí)行結(jié)果存在二義性,就是線程安全的。
線程安全問題都是由全局變量及靜態(tài)變量引起的。
若每個線程中對全局變量,靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若多個線程同時執(zhí)行寫操作,一般都需要考慮線程同步,否則就可能影響線程安全。
- 常量始終是線程安全的,因為只存在讀操作;
- 每次調(diào)用方法前都新建一個實例是線程安全的,因為不會訪問共享的資源;
- 局部變量是線程安全的。因為每執(zhí)行一個方法,都會在獨立的空間創(chuàng)建局部變量,它不是共享資源。局部變量包括方法的參數(shù)變量和方法內(nèi)的變量。
在關(guān)于spring單例與線程安全的很多文章中,會提到一個概念,即有狀態(tài)bean和無狀態(tài)bean。
無狀態(tài)bean:無狀態(tài),就是一次操作,不能保存數(shù)據(jù)。無狀態(tài)bean,就是沒有實例變量的對象,不能保存數(shù)據(jù),是不變類,在線程安全的。
有狀態(tài)bean:有狀態(tài),就是有數(shù)據(jù)存儲功能。有狀態(tài)bean,就是有實例變量的對象,可以保存數(shù)據(jù),是非線程安全的。
如何解決線程安全問題?
(1)使用線程同步機制:通過對象的鎖機制保證同一時間只有一個線程訪問變量。這時該變量是多個線程共享的,使用同步機制要求程序縝密地分析什么時候?qū)ψ兞窟M行讀寫,什么時候需要鎖定某個對象,什么時候釋放對象鎖等繁雜問題,程序設(shè)計和編寫難度相對較大。
(2)使用ThreadLocal:為每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數(shù)據(jù)的訪問沖突。因為每一個線程都擁有自己的變量副本,從而也就沒有必要對該變量進行同步了。
概括起來就是:對于多線程資源共享的問題,同步機制采用了“以時間換空間”的方式,而ThreadLocal采用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而后者為每一個線程都提供了一份變量,因此可以同時訪問而互不影響。
5、單例如何變多例
Scope聲明為prototype,即
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
SpringMVC單例和多例的優(yōu)缺點
默認是單例模式可設(shè)置為多例模式,兩種模式的優(yōu)缺點:
1、單例模式(單例多線程的)
- 優(yōu)點:共享一個實例,內(nèi)存占用少,GC開銷小
- 缺點:共享資源存在線程安全問題
2、多例模式(單線程的)
- 優(yōu)點:不存在線程安全問題(靜態(tài)共享資源除外)
- 缺點:多個實例,內(nèi)存占用多,GC開銷大
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java橋梁設(shè)計模式優(yōu)雅地將抽象與實現(xiàn)分離
Java橋接設(shè)計模式通過將抽象和實現(xiàn)分離,使得它們可以獨立地變化,從而實現(xiàn)更靈活的代碼結(jié)構(gòu)。它是一種優(yōu)雅的設(shè)計模式,適用于需要處理多個變化因素的復(fù)雜應(yīng)用程序2023-04-04java實現(xiàn)學(xué)生成績檔案管理系統(tǒng)
這篇文章主要為大家詳細介紹了java實現(xiàn)學(xué)生成績檔案管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-07-07Java連接并操作Sedna XML數(shù)據(jù)庫的方法
這篇文章主要介紹了Java連接并操作Sedna XML數(shù)據(jù)庫的方法,較為詳細的說明了Sedna XML數(shù)據(jù)庫的原理與功能,并給出了基于java操作Sedna XML數(shù)據(jù)庫的方法,需要的朋友可以參考下2015-06-06Java將Exception信息轉(zhuǎn)為String字符串的方法
今天小編就為大家分享一篇Java將Exception信息轉(zhuǎn)為String字符串的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10Java Web開發(fā)防止多用戶重復(fù)登錄的完美解決方案
在web項目開發(fā)中,很多情況下都可以讓同一個賬號信息在不同的登錄入口登錄很多次,這樣子做的不是很完善。一般解決這種情況有兩種解決方案,小編呢主要以第二種方式給大家介紹具體的實現(xiàn)方法,對java web 防止多用戶重復(fù)登錄的解決方案感興趣的朋友一起看看吧2016-11-11使用@PathVariable時候無法將參數(shù)映射到變量中的解決
這篇文章主要介紹了使用@PathVariable時候無法將參數(shù)映射到變量中的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08