設(shè)計模式之單例模式

單例模式定義

確保某一個類之后一個實例,而且自行實例化并向整個系統(tǒng)提供服務(wù)。

單例模式使用場景

確保某個類有且只有一個對象的場景,避免產(chǎn)生多個對象消耗過多的資源,或者某種類型的對象只應(yīng)該有且只有一個。例如,創(chuàng)建一個對象需要消耗的資源過多,如要訪問IO和數(shù)據(jù)庫等資源,這時就要考慮使用單例模式。

單例模UML類圖

singleton.png

角色介紹:

  • Client - 客戶端
  • Singleton - 單例類

實現(xiàn)單例模式主要注意的關(guān)鍵點:

  • 構(gòu)造函數(shù)不能對外開放,一般為Private
  • 通過一個靜態(tài)方法或者枚舉返回單例類對象;
  • 確保單例類的對象有且只有一個,尤其是在多線程的環(huán)境下;
  • 確保單例類對象在反序列化時不會重新構(gòu)建對象。

通過將單例類的構(gòu)造方法私有化,使得客戶端代碼不能通過new 的形式手動構(gòu)造單例類的對象,單例類會暴露一個公有的靜態(tài)方法,客戶端需要調(diào)用這個靜態(tài)方法獲取到單例類的唯一對象,在獲取這個單例類對象的過程中需要確保線程安全,即在多線程環(huán)境下構(gòu)造單例類的對象也是有且只有一個,這也是單例模式實現(xiàn)中比較困難的地方。

單例類的實現(xiàn)

1.餓漢式單例

public class Singleton{

    /**
     * 餓漢式單例
     */
    private static final Singleton singleton = new Singleton();

    /**
     * 私有化構(gòu)造方法
     */
    private Singleton() {

    }

    public static Singleton getSingleton() {
        return singleton;
    }
}

2.懶漢式單例

懶漢模式是聲明一個靜態(tài)對象,并且在用戶第一次調(diào)用getSingleton() 時進行初始化,而上述餓漢模式是在聲明靜態(tài)對象時就已經(jīng)初始化。懶漢模式實現(xiàn):

public class LazySingleton {
    private static LazySingleton instance;
    private LazySingleton(){
    }

    public static synchronized LazySingleton getInstance(){
        if (instance==null){
            instance = new LazySingleton();
        }
        return instance;
    }
}

getInstance() 是用關(guān)鍵字synchronized 修飾,是一個同步方法,以確保多線程情況下單例類對象唯一性。但是每次調(diào)用getInstance() 時都會進行同步,這樣會消耗不必要的資源,這也是懶漢式單例的最大問題。懶漢式單例模式優(yōu)點是單例只有在使用時才會被初始化,在一定程度上節(jié)約了資源;缺點是第一次加載是需要及時進行實例化,反應(yīng)稍慢。

3.Double Check Lock(DCL)實現(xiàn)單例

DCL 方式實現(xiàn)單例模式的優(yōu)點是既能夠在需要是才初始化,又能保證線程安全,且單例對象初始化后調(diào)用getInstance() 不進行同步鎖。實現(xiàn)如下:

public class DCLSingleton {
    private static DCLSingleton singleton;

    private DCLSingleton() {
    }

    public static DCLSingleton getSingleton() {
        if (singleton == null) {
            synchronized (DCLSingleton.class) {
                if (singleton == null) {
                    singleton = new DCLSingleton();
                }
            }
        }
        return singleton;
    }
}

DCL模式是使用最多的單例實現(xiàn)方式,它能夠在需要是才實例化單例對象,并且能夠保證對象的唯一性。

4.靜態(tài)內(nèi)部類單例模式

public class StaticInnerSingleton {
    public static StaticInnerSingleton getSingleton() {
        return SingletonHelp.SINGLRTON;
    }

    private static class SingletonHelp {
        private static final StaticInnerSingleton SINGLRTON = new StaticInnerSingleton();
    }
}

當(dāng)?shù)谝淮渭虞dStaticInnerSingleton 類時并不會初始化SINGLRTON ,只有在第一次調(diào)用getSingleton() 時才會導(dǎo)致初始化。因此,第一次調(diào)用getSingleton()會導(dǎo)致虛擬機加載 SingletonHelp 類,這種方式不僅能保證線程安全,也能保證單例對象的唯一性,同時延遲了單例的實例化,所以推薦使用這種方式實現(xiàn)單例模式。

5.枚舉單例

public enum EnumSingleton {
    INSINGLE;
}

寫法簡單是枚舉單例的最大優(yōu)點,枚舉在Java 中與普通類一樣,不僅能夠有字段,還能夠有自己的方法。最重要的是默認枚舉實例的創(chuàng)建是線程安全的,并且任何情況下它都是一個單例。

6.使用容器實現(xiàn)單例

public class SingletonManager {
    private static HashMap<String, Object> objMap = new HashMap<>();

    private SingletonManager() {

    }

    public static void registerService(String key, Object value) {
        if (!objMap.containsKey(key)) {
            objMap.put(key, value);
        }
    }

    public Object get(String key) {
        return objMap.get(key);
    }
}

在程序的初始,將多種單例類型注入到一個統(tǒng)一的管理類中,在使用是根據(jù)key 獲取對應(yīng)類型的對象。這種方式使得我們可以管理多種類型的單例,并且使用時可以通過統(tǒng)一的接口進行獲取操作,降低了用戶的使用成本,也對用戶隱藏了具體實現(xiàn),降低了耦合度。


總結(jié)

不管以那種方式實現(xiàn)單例模式,他們的核心都是將構(gòu)造函數(shù)私有化,并且通過靜態(tài)方法獲取一個唯一的實例,在這個獲取的過程中必須保證線程安全、防止反序列化導(dǎo)致重新生成實例對象等問題。但是由于客戶端通常沒有高并發(fā)的情況,因此,選擇哪種方式實現(xiàn)并不會有太大影響。即便如此,出于效率考慮,推薦使用DCL方式和靜態(tài)內(nèi)部類方式。

優(yōu)點

  • 由于單例模式在內(nèi)存中只有一個實例,減少了內(nèi)存開支,特別是一個對象需要頻繁的創(chuàng)建銷毀時,而且創(chuàng)建和銷毀時性能又無法優(yōu)化,單例模式的優(yōu)勢就非常明顯。
  • 由于單例模式只生成一個實例,減少了系統(tǒng)的性能開銷,當(dāng)一個對象的產(chǎn)生需要比較多的資源時,如讀取配置、產(chǎn)生其他依賴對象時,則可以通過在應(yīng)用啟動是直接產(chǎn)生一個單例對象,然后永久駐留內(nèi)存的方式解決。
  • 單例模式可以避免對資源的多重占用,例如一個寫文件操作,由于只有一個實例存在內(nèi)存中,避免對同一資源文件的同時寫操作。
  • 單例模式可以在系統(tǒng)設(shè)置全局的訪問點,優(yōu)化和共享資源訪問,例如,可以設(shè)計一個單例類,負責(zé)所有數(shù)據(jù)表的映射處理。

缺點

  • 單例模式一般沒有接口,擴展很困難。
  • 單例對象如果持有Context,那么很容易引發(fā)內(nèi)存泄露,此時需要注意傳遞對象的Context最后是Application Context 。

Demo

設(shè)計模式Demo

Android源碼單例模式

Android源碼單例模式

參考

《Android源碼設(shè)計模式》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容