【設計模式筆記】建立型--單例模式

單例模式(Singleton)

確保一個類只有一個實例,而且自行實例化並向系統提供這個實例;提供全局訪問的方法java

常見場景:windows的任務管理器(單實例)windows

結構圖

 

上面的getInstance方法在多線程狀況下,或致使屢次new實例,從而使用方獲得的不是同一份實例安全

單例模式--餓漢式(多線程問題解決方式1)

 

代碼樣例:多線程

/**
 * 餓漢式單例:類加載式就啓動實例的初始化,保證使用時實例已建立完畢,從而全部適用方都獲取同一份實例
 * @author clari
 *
 */
public class EagerSingleton {
    
    // 類加載時,隨即被實例化
    private static EagerSingleton instance = new EagerSingleton();
    
    private EagerSingleton()
    {
        
    }
    
    public static EagerSingleton getInstance()
    {
        return instance;
    }
}

單例模式--懶漢式單例(解決多線程問題)

 

 代碼樣例性能

/**
 *  懶漢式單例
 *  第一次使用時才建立,延遲加載,可能資源初始化耗時
 * @author clari
 *
 */
public class LazySingleton {

    private volatile static LazySingleton instance = null;
    
    private LazySingleton()
    {
        
    }
    
    // 雙重檢查鎖定:同時增長volatile確保成員變量能夠對多個線程正確處理
    private static LazySingleton getInstance()
    {
        if (null == instance) {
            synchronized (LazySingleton.class) {
                if (null == instance) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

 

餓漢式 vs 懶漢式

餓漢式:類加載時已實例化,不用考慮多線程訪問問題。可是無論實例最終是否會使用,都會提早實例化,可能形成資源的浪費;同時系統加載時需實例化,可能形成系統加載緩慢;spa

懶漢式:第一次使用時實例化,不提早預佔資源,必須處理好多個線程訪問的問題。實例化時可能會耗費較長時間,意味着多線程同時訪問的概率更大,須要雙重檢查致使系統性能獲得損失。線程

 

更優的單例解決方法

IoDH: Initialization on Demand Holder: 類中增長一個靜態內部類,內部類建立實例對象。code

第一次調用getInstance()方法時加載內部類,此時初始化該內部類的靜態對象instance,由Java虛擬機保證內部類初始化實例的的線程安全性,確保只初始化一次。對象

既實現延遲加載,又保證線程安全,不影響系統性能(getInstance()方法沒有任何線程綁定)。blog

public class BetterSingleton {

    private BetterSingleton()
    {
        
    }
    
    private static class HolderClass
    {
        private final static BetterSingleton instance = new BetterSingleton();
    }
    
    private static BetterSingleton getInstance()
    {
        return HolderClass.instance;
    }
    
    public static void main(String[] args) {
        BetterSingleton  s1, s2;
        s1 = BetterSingleton.getInstance();
        s2 = BetterSingleton.getInstance();
        
        System.out.println("s1 == s2: " + (s1==s2));
    }

}

 

 單例的缺點

一、沒有抽象層,擴展存在困難;

二、職責太重,必定程度違背單一職責原則,同時提供業務方法,也提供建立對象方法,將對象建立和訪問耦合一塊兒。

三、java實現了垃圾自動回收,若是實例化對象長期不適用,可能會被垃圾回收,後面再適用再次從新實例化,則可能致使共享的單例對象狀態的丟失。

相關文章
相關標籤/搜索