Spring中的單例和多例的區別


1,什么是單例和多例
單例:所有請求用同一個對象來處理。通過單例模式,可以保證系統中一個類只有一個實例。
多例:每個請求用一個新的對象來處理。

2,Spring中的單例與多例
spring ioc容器的bean都是默認單例的,即spring依賴注入Bean實例默認是單例的。

spring提供了5中scope,分別是singleton,prototype,request,session,global session,常用是前兩種。

單例bean與多例(原型)bean的區別
如果一個bean被聲明為單例的時候,在處理多次請求的時候,在spring容器里只實例化出一個bean,后續的請求都公用這個對象,這個對象會保存在一個map里面。當有請求來的時候,會先從緩存(map)里查看有沒有,有的話直接使用這個對象,沒有的話才實例化一個新的對象,所以這是個單例的。但是對於原型(prototype)bean來說,當每次請求來的時候,會直接實例化新的bean,沒有緩存以及緩存查詢的過程。

3. 為什么用單例、多例:
    之所以用單例,是因為沒必要每個請求都新建一個對象,這樣子既浪費CPU又浪費內存;
   之所以用多例,是為了防止並發問題;即一個請求改變了對象的狀態,此時對象又處理另一個請求,而之前請求對對象狀態的改變導致了對象對另一個請求做了錯誤的處理;
    用單例和多例的標准只有一個:
    當對象含有可改變的狀態時(更精確的說就是在實際應用中該狀態會改變),則多例,否則單例;

4,單例的優勢與劣勢
優勢
由於不會創建新的對象,所以有以下幾個性能上的優勢:

  • 減少新生成實例的消耗。新生成實例包括兩個方面,第一,spring會通過反射或者cglib來生成bean實例,這都是耗性能的操作。第二,給對象分配內存也會涉及負責算法。
  • 減少jvm垃圾回收。由於不會給每個請求都生成bean實例,所以回收的對象就少了。
  • 可以快速獲取到bean。因為單例獲取bean操作,除了第一次生成之外,其余都是從緩存里獲取的,所以很快。

劣勢:
一個很大的劣勢是它不能做到線程安全。由於所有請求都共享一個bean實例,那么如果這個bean是一個有狀態的bean的話,在並發場景下就有可能出現問題。

5,spring單例模式與線程安全:
當多用戶同時請求一個服務時,容器會給每一個請求分配一個線程,這時多個線程會並發執行該請求所對應的業務邏輯(成員方法),此時就要注意了,如果該處理邏輯中有對該單例狀態的修改(體現為該單例的成員屬性),則必須考慮線程同步問題(此時該狀態就是一個臨界資源(共享數據),如果多個線程同時操作(修改)這個臨界資源就會誘發線程安全問題)。

線程安全:如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行的結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。或者說:一個類或者程序所提供的接口對於線程來說是原子操作,或者多線程之間的切換不會導致該接口的執行結果存在二義性,就是線程安全的。

線程安全問題都是由全局變量及靜態變量引起的。
若每個線程中對全局變量,靜態變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若多個線程同時執行寫操作,一般都需要考慮線程同步,否則就可能影響線程安全。

  • 常量始終是線程安全的,因為只存在讀操作;
  • 每次調用方法前都新建一個實例是線程安全的,因為不會訪問共享的資源;
  • 局部變量是線程安全的。因為每執行一個方法,都會在獨立的空間創建局部變量,它不是共享資源。局部變量包括方法的參數變量和方法內的變量。

在關於spring單例與線程安全的很多文章中,會提到一個概念,即有狀態bean和無狀態bean。
無狀態bean:無狀態,就是一次操作,不能保存數據。無狀態bean,就是沒有實例變量的對象,不能保存數據,是不變類,在線程安全的。
有狀態bean:有狀態,就是有數據存儲功能。有狀態bean,就是有實例變量的對象,可以保存數據,是非線程安全的。

如何解決線程安全問題?
(1)使用線程同步機制:通過對象的鎖機制保證同一時間只有一個線程訪問變量。這時該變量是多個線程共享的,使用同步機制要求程序縝密地分析什么時候對變量進行讀寫,什么時候需要鎖定某個對象,什么時候釋放對象鎖等繁雜問題,程序設計和編寫難度相對較大。
(2)使用ThreadLocal:為每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的訪問沖突。因為每一個線程都擁有自己的變量副本,從而也就沒有必要對該變量進行同步了。

概括起來就是:對於多線程資源共享的問題,同步機制采用了“以時間換空間”的方式,而ThreadLocal采用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而后者為每一個線程都提供了一份變量,因此可以同時訪問而互不影響。

6,單例如何變多例
Scope聲明為prototype,即

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM