IoDH實現的單例模式
在別人的程式碼裡,看到用了一種很奇葩的方式,實現的單例模式,後來搜尋了下這樣實現的原因,才知道這是一個叫Initialization Demand Holder (IoDH)的技術,轉了兩篇,和大家分享下。
原文地址1
原文地址2
3.4 餓漢式單例與懶漢式單例的討論
Sunny公 司開發人員使用單例模式實現了負載均衡器的設計,但是在實際使用中出現了一個非常嚴重的問題,當負載均衡器在啟動過程中使用者再次啟動該負載均衡器時,系統 無任何異常,但當客戶端提交請求時出現請求分發失敗,通過仔細分析發現原來系統中還是存在多個負載均衡器物件,導致分發時目標伺服器不一致,從而產生衝 突。為什麼會這樣呢?Sunny
現在我們對負載均衡器的實現程式碼進行再次分析,當第一次呼叫getLoadBalancer()方法建立並啟動負載均衡器時,instance物件為null值,因此係統將執行程式碼instance= new LoadBalancer(),在此過程中,由於要對LoadBalancer進行大量初始化工作,需要一段時間來建立LoadBalancer物件。而在此時,如果再一次呼叫getLoadBalancer()方法(通常發生在多執行緒環境中),由於instance尚未建立成功,仍為null值,判斷條件(instance==
null)為真值,因此程式碼instance= new LoadBalancer()
如何解決該問題?我們至少有兩種解決方案,在正式介紹這兩種解決方案之前,先介紹一下單例類的兩種不同實現方式,餓漢式單例類和懶漢式單例類。
1.餓漢式單例類
餓漢式單例類是實現起來最簡單的單例類,餓漢式單例類結構圖如圖3-4所示:
從圖3-4中可以看出,由於在定義靜態變數的時候例項化單例類,因此在類載入的時候就已經建立了單例物件,程式碼如下所示:- class EagerSingleton {
- privatestaticfinal EagerSingleton instance =
- private EagerSingleton() { }
- publicstatic EagerSingleton getInstance() {
- return instance;
- }
- }
2.懶漢式單例類與執行緒鎖定
除了餓漢式單例,還有一種經典的懶漢式單例,也就是前面的負載均衡器LoadBalancer類的實現方式。懶漢式單例類結構圖如圖3-5所示:
從圖3-5中可以看出,懶漢式單例在第一次呼叫getInstance()方法時例項化,在類載入時並不自行例項化,這種技術又稱為延遲載入(Lazy Load)技術,即需要的時候再載入例項,為了避免多個執行緒同時呼叫getInstance()方法,我們可以使用關鍵字synchronized,程式碼如下所示:- class LazySingleton {
- privatestatic LazySingleton instance = null;
- private LazySingleton() { }
- synchronizedpublicstatic LazySingleton getInstance() {
- if (instance == null) {
- instance = new LazySingleton();
- }
- return instance;
- }
- }
- publicstatic LazySingleton getInstance() {
- if (instance == null) {
- synchronized (LazySingleton.class) {
- instance = new LazySingleton();
- }
- }
- return instance;
- }
假如在某一瞬間執行緒A和執行緒B都在呼叫getInstance()方法,此時instance物件為null值,均能通過instance == null的判斷。由於實現了synchronized加鎖機制,執行緒A進入synchronized鎖定的程式碼中執行例項建立程式碼,執行緒B處於排隊等待狀態,必須等待執行緒A執行完畢後才可以進入synchronized鎖定程式碼。但當A執行完畢時,執行緒B並不知道例項已經建立,將繼續建立新的例項,導致產生多個單例物件,違背單例模式的設計思想,因此需要進行進一步改進,在synchronized中再進行一次(instance == null)判斷,這種方式稱為雙重檢查鎖定(Double-Check Locking)。使用雙重檢查鎖定實現的懶漢式單例類完整程式碼如下所示:
- class LazySingleton {
- privatevolatilestatic LazySingleton instance = null;
- private LazySingleton() { }
- publicstatic LazySingleton getInstance() {
- //第一重判斷
- if (instance == null) {
- //鎖定程式碼塊
- synchronized (LazySingleton.class) {
- //第二重判斷
- if (instance == null) {
- instance = new LazySingleton(); //建立單例例項
- }
- }
- }
- return instance;
- }
- }
需要注意的是,如果使用雙重檢查鎖定來實現懶漢式單例類,需要在靜態成員變數instance之前增加修飾符volatile,被volatile修飾的成員變數可以確保多個執行緒都能夠正確處理,且該程式碼只能在JDK 1.5及以上版本中才能正確執行。由於volatile關鍵字會遮蔽Java虛擬機器所做的一些程式碼優化,可能會導致系統執行效率降低,因此即使使用雙重檢查鎖定來實現單例模式也不是一種完美的實現方式。
|
3.餓漢式單例類與懶漢式單例類比較
餓漢式單例類在類被載入時就將自己例項化,它的優點在於無須考慮多執行緒訪問問題,可以確保例項的唯一性;從呼叫速度和反應時間角度來講,由於單例物件一開 始就得以建立,因此要優於懶漢式單例。但是無論系統在執行時是否需要使用該單例物件,由於在類載入時該物件就需要建立,因此從資源利用效率角度來講,餓漢 式單例不及懶漢式單例,而且在系統載入時由於需要建立餓漢式單例物件,載入時間可能會比較長。
懶漢式單例類在第一次使用時建立,無須一直佔用系統資源,實現了延遲載入,但是必須處理好多個執行緒同時訪問的問題,特別是當單例類作為資源控制器,在例項 化時必然涉及資源初始化,而資源初始化很有可能耗費大量時間,這意味著出現多執行緒同時首次引用此類的機率變得較大,需要通過雙重檢查鎖定等機制進行控制, 這將導致系統性能受到一定影響。
————————————————
3.5 一種更好的單例實現方法
餓漢式單例類不能實現延遲載入,不管將來用不用始終佔據記憶體;懶漢式單例類執行緒安全控制煩瑣,而且效能受影響。可見,無論是餓漢式單例還是懶漢式單例都存在這樣那樣的問題,有沒有一種方法,能夠將兩種單例的缺點都克服,而將兩者的優點合二為一呢?答案是:Yes!下面我們來學習這種更好的被稱之為Initialization Demand Holder (IoDH)的技術。
在IoDH中,我們在單例類中增加一個靜態(static)內部類,在該內部類中建立單例物件,再將該單例物件通過getInstance()方法返回給外部使用,實現程式碼如下所示:
- //Initialization on Demand Holder
- class Singleton {
- private Singleton() {
- }
- privatestaticclass HolderClass {
- privatefinalstatic Singleton instance = new Singleton();
- }
- publicstatic Singleton getInstance() {
- return HolderClass.instance;
- }
- publicstaticvoid main(String args[]) {
- Singleton s1, s2;
- s1 = Singleton.getInstance();
- s2 = Singleton.getInstance();
- System.out.println(s1==s2);
- }
- }
編譯並執行上述程式碼,執行結果為:true,即建立的單例物件s1和s2為同一物件。由於靜態單例物件沒有作為Singleton的成員變數直接例項化,因此類載入時不會例項化Singleton,第一次呼叫getInstance()時將載入內部類HolderClass,在該內部類中定義了一個static型別的變數instance,此時會首先初始化這個成員變數,由Java虛擬機器來保證其執行緒安全性,確保該成員變數只能初始化一次。由於getInstance()方法沒有任何執行緒鎖定,因此其效能不會造成任何影響。
通過使用IoDH,我們既可以實現延遲載入,又可以保證執行緒安全,不影響系統性能,不失為一種最好的Java語言單例模式實現方式(其缺點是與程式語言本身的特性相關,很多面向物件語言不支援IoDH)。
|
至此,三種單例類的實現方式我們均已學習完畢,它們分別是餓漢式單例、懶漢式單例以及IoDH。
相關推薦
溫故而知新(java實現)單例模式的七種寫法
反序 防止 代碼 工作 html 我想 變種 evel 才會 第一種(懶漢,線程不安全): Java代碼 public class Singleton { private static Singleton instance; private S
Java枚舉enum以及應用:枚舉實現單例模式
tee configure adr 自由 這樣的 pre 單例模式 做到 build 枚舉作為一個常規的語言概念,一直到Java5才誕生不得不說有點奇怪,以至於到現在為止很多程序員仍然更喜歡用static final的形式去命名常量而不使用,一般情況下,Java程序員用這種
Python 實現單例模式
python 單例模式 Python 實現單例模式 # 使用Python實現單例模式 # 方法一: 使用__new__方法 class SingleTon(object): def __new__(cls, *args, **kwargs): if not ha
使用靜態代碼塊來實現單例模式
對象 代碼 over 說明 override out min .get admin package com.wz.thread.staticlump;/** * 使用靜態代碼塊來實現單例模式 * @author Administrator * */public class
枚舉實現單例模式
min () bject oca serve oid ros manager [] package com.wz.thread.enums;import java.sql.Connection;import java.sql.DriverManager;/** * 使用枚舉
python實現單例模式
一次 color 中大 我們 浪費 python實現 判斷 內存 () 有這麽一種場景,我們把數據封裝到類體或類的某個方法裏,然而我們new出這個類只是為了拿到這部分數據,那麽當多次這樣調用的時候,每次都來拿數據並放到內存中大大浪費了內存。 那我們就可以想,我們拿到一次數據
python3實現單例模式
單例模式 python3 單例模式指確保某個類在整個系統中只存在一個實例的一種設計模式使用單例模式的好處:1、每個實例都會占用一定的內存資源,且初始化實例時會影響運行性能,所以當整個系統只需一個實例時,使用單例模式不僅可減少資源占用,而且因為只初始化一次,還可以加快運行性能。例如當程序通過一個類來讀取
雙重檢查鎖實現單例模式的線程安全問題
多線程 urn blog 內存 http 代碼 地方 gets 技術博客 一、結論 雙重校驗鎖的單例模式代碼如下: public class Singleton { private static Singleton singleton; private Singl
python 使用裝飾器實現單例模式
1、裝飾器 python中一切皆物件,同樣函式也是一個物件。函式物件有一個__name__屬性,可以檢視函式的名字。 def demo(): print("xxx") print(demo.__name__) output: demo 假設我們要增強函式dem
AtomicReference實現單例模式
CAS是項樂觀鎖技術,當多個執行緒嘗試使用CAS同時更新同一個變數時,只有其中一個執行緒能更新變數的值,而其它執行緒都失敗,失敗的執行緒並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試。 樂觀鎖的一種實現方式——CAS 在JDK1.5 中新增java.util.concurrent(
使用boost庫實現單例模式
單例模式是最常使用的設計模式之一,特別在跨檔案程式設計時使用的最多。 #include<boost/noncopyable.hpp> #include<pthread.h> #include<stdlib.h> template<typename T
用單例物件實現單例模式
2018-12-04 14:55:07 object SingletonDemo { //object修飾的為物件 def main(args: Array[String]): Unit = { val s = SessionFactory println(s.getS
利用c++的private和static實現單例模式
精髓就是 將建構函式設定為private屬性,並且將複製建構函式和賦值建構函式也設定為private屬性,這樣的話,就無法在外部建立物件,所以此時還需要一個public的函式:getHumanInterface(),這個函式來呼叫private屬性的建構函式來生成我們需要的物件,並且將這個物
Effective Java 第二版 中文版 筆記(三)列舉實現單例模式
列舉實現單例模式: public enum DataSourceEnum { DATASOURCE; private DBConnection connection = null; private DataSourceEnum() {
雙重檢查鎖定實現單例模式與延遲載入化
首先我們從單例模式開始: 一、單例模式定義: 單例模式確保某個類只有一個例項,而且自行例項化並向整個系統提供這個例項。在計算機系統中,執行緒池、快取、日誌物件、對話方塊、印表機、顯示卡的驅動程式物件常被設計成單例。這些應用都或多或少具有資源管理器的功能。每臺計算機可以有若干個印表機,但只能有
python中用裝飾器實現單例模式
def singleton(cls,*args,**kwargs): instances = {} def get_instance(*args,**kwargs): if cls not in instances: i
通過 python的 __call__ 函式與元類 實現單例模式
簡單一句話,當一個類實現__call__方法時,這個類的例項就會變成可呼叫物件。 直接上測試程式碼 class ClassA: def __call__(self, *args, **kwargs): print('call ClassA i
python中實現單例模式
單例模式(Singleton Pattern)是一種常用的軟體設計模式,該模式的主要目的是確保某一個類只有一個例項存在。當你希望在整個系統中,某個類只能出現一個例項時,單例物件就能派上用場。 比如,某個伺服器程式的配置資訊存放在一個檔案中,客戶端通過一個 AppConfig
Java面試題 實現單例模式
package com.coderli.interview; /** * 常見面試題:實現單例模式 <br> * 《劍指offer》,第二章,面試題2 <br> * 這裡給出五種寫法和對應的評論 * * @author lihzh * @date 2014年8月12日 上午11:10
內部類實現單例模式
以前java的學習中對於單例模式是再熟悉不過了,對於單例模式的實現方法也是再熟悉不過了。通常設計模式中交給我們懶漢式還有餓漢式兩種實現方法,程式碼如下: public class Sin