java singleton模式四種線程安全的實現

1.描述:java

    Singleton(單例)是設計模式的一種,爲了保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。設計模式

2.主要特色:     緩存

    1)單例類確保本身只有一個實例(構造函數私有:不被外部實例化,也不被繼承)。網絡

    2)單例類必須本身建立本身的實例。app

    3)單例類必須爲其餘對象提供惟一的實例。jvm

3.單例模式的應用:函數

    資源管理器,回收站,打印機資源,線程池,緩存,配置信息類,管理類,控制類,門面類,代理類一般被設計爲單例類性能

    若是程序有多個類加載器又同時使用單例模式就有可能多個單例並存就要找相應解決方法了優化

4.實現方法:.net

若是應用程序老是建立並使用單例實例或在建立和運行時開銷不大。

1).Eager initialization 餓漢式單例類(依賴jvm在加載類時建立惟一單例實例)

public class EagerSingleton {  
        // jvm保證在任何線程訪問uniqueInstance靜態變量以前必定先建立了此實例  
        private static EagerSingleton uniqueInstance = new EagerSingleton();  
  
        // 私有的默認構造子,保證外界沒法直接實例化  
        private EagerSingleton() {  
        }  
  
        // 提供全局訪問點獲取惟一的實例  
        public static EagerSingleton getInstance() {  
                return uniqueInstance;  
        }  
}

若是開銷比較大,但願用到時才建立就要考慮延遲實例化,或者Singleton的初始化須要某些外部資源(好比網絡或存儲設備),就要用後面的方法了.

 

2)Lazy initialization 懶漢式單例類

public class LazySingleton {  
        private static LazySingleton uniqueInstance;  
  
        private LazySingleton() {  
        }  
  
        public static synchronized LazySingleton getInstance() {  
                if (uniqueInstance == null)  
                        uniqueInstance = new LazySingleton();  
                return uniqueInstance;  
        }  
}

同步一個方法可能形成程序執行效率降低100倍,徹底沒有必要每次調用getInstance都加鎖,事實上咱們只想保證一次初始化成功,其他的快速返回而已,若是在getInstance頻繁使用的地方就要考慮從新優化了.

 

3)"雙檢鎖"(Double-Checked Lock)儘可能將"加鎖"推遲,只在須要時"加鎖"(僅適用於Java 5.0 以上版本,volatile保證原子操做) 
happens-before:"什麼什麼必定在什麼什麼以前運行",也就是保證順序性.
如今的CPU有亂序執行的能力(也就是指令會亂序或並行運行,能夠不按咱們寫代碼的順序執行內存的存取過程),而且多個CPU之間的緩存也不保證明時同步,只有上面的happens-before所規定的狀況下才保證順序性.

JVM可以根據CPU的特性(CPU的多級緩存系統、多核處理器等)適當的從新排序機器指令,使機器指令更符合CPU的執行特色,最大限度的發揮機器的性能.

若是沒有volatile修飾符則可能出現一個線程t1的B操做和另外一線程t2的C操做之間對instance的讀寫沒有happens-before,可能會形成的現象是t1的B操做尚未徹底構形成功,但t2的C已經看到instance爲非空,這樣t2就直接返回了未徹底構造的instance的引用,t2想對instance進行操做就會出問題.

    volatile 的功能:
1. 避免編譯器將變量緩存在寄存器裏  
2. 避免編譯器調整代碼執行的順序

優化器在用到這個變量時必須每次都當心地從新讀取這個變量的值,而不是使用保存在寄存器裏的備份。

public class DoubleCheckedLockingSingleton {  
        // java中使用雙重檢查鎖定機制,因爲Java編譯器和JIT的優化的緣由系統沒法保證咱們指望的執行次序。  
        // 在java5.0修改了內存模型,使用volatile聲明的變量能夠強制屏蔽編譯器和JIT的優化工做  
        private volatile static DoubleCheckedLockingSingleton uniqueInstance;  
  
        private DoubleCheckedLockingSingleton() {  
        }  
  
        public static DoubleCheckedLockingSingleton getInstance() {  
                if (uniqueInstance == null) {  
                        synchronized (DoubleCheckedLockingSingleton.class) {  
                                if (uniqueInstance == null) {  
                                        uniqueInstance = new DoubleCheckedLockingSingleton();  
                                }  
                        }  
                }  
                return uniqueInstance;  
        }  
}

4)Lazy initialization holder class 知足全部 Double-Checked Locking 知足的條件,而且沒有顯示的同步操做

public class LazyInitHolderSingleton {  
        private LazyInitHolderSingleton() {  
        }  
  
        private static class SingletonHolder {  
                private static final LazyInitHolderSingleton INSTANCE = new LazyInitHolderSingleton();  
        }  
  
        public static LazyInitHolderSingleton getInstance() {  
                return SingletonHolder.INSTANCE;  
        }  
}

根據jvm規範,當某對象第一次調用LazyInitHolderSingleton.getInstance()時,LazyInitHolderSingleton類被首次主動使用,jvm對其進行初始化(此時並不會調用LazyInitHolderSingleton()構造方法),而後LazyInitHolderSingleton調用getInstance()方法,該方法中,又首次主動使用了SingletonHolder類,因此要對SingletonHolder類進行初始化,初始化中,INSTANCE常量被賦值時才調用了 LazyInitHolderSingleton的構造方法LazyInitHolderSingleton(),完成了實例化並返回該實例。

當再有對象(也許是在別的線程中)再次調用LazyInitHolderSingleton.getInstance()時,由於已經初始化過了,不會再進行初始化步驟,因此直接返回INSTANCE常量即同一個LazyInitHolderSingleton實例。

相關文章
相關標籤/搜索