單例模式——應(yīng)用最廣的模式

導(dǎo)語

單例模式是應(yīng)用最廣的模式,在應(yīng)用這個模式時,單例對象的類必須保證只有一個實例存在。許多時候整個系統(tǒng)只需要擁有一個全局對象,這樣有利于我們協(xié)調(diào)系統(tǒng)整體的行為。

主要內(nèi)容

  • 單例模式的定義
  • 單例模式的使用場景
  • 單例模式的UML類圖
  • 單例模式的實現(xiàn)方式

具體內(nèi)容

單例模式的定義

確保某一個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例。

單例模式的使用場景

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

單例模式的UML類圖

單例模式的UML類圖如下所示。

單例模式的UML類圖

角色介紹:

  • Client——高層客戶端。
  • Singleton——單例類。

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

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

單例模式的實現(xiàn)方式

單例模式的實現(xiàn)有如下幾種:
推薦使用DCL單例(雙重檢查鎖定)和靜態(tài)內(nèi)部類單例模式。

  • 餓漢模式
  • 懶漢模式(線程不安全)
  • 懶漢模式(線程安全)
  • DCL單例(雙重檢查鎖定)
  • 靜態(tài)內(nèi)部類單例模式
  • 枚舉單例
  • 使用容器實現(xiàn)單例模式
餓漢模式
public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton(){
    }

    public static Singleton getInstance() {
        return instance;
    }
 }

這種寫法是在類裝載時就實例化instance,他避免了多線程的同步問題。但是不能保證有別的方式去裝載,沒有達到懶加載(延遲加載)。

懶漢模式(線程不安全)
public class Singleton {
    private static Singleton instance;

    private Singleton (){
    }

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

達到了懶加載,但是在多線程不能正常工作。

懶漢模式(線程安全)
public class Singleton {
    private static Singleton instance;

    private Singleton(){
    }

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

這種寫法能夠在多線程中很好的工作,但是每次調(diào)用getInstance方法都會進行同步,反應(yīng)稍慢,還會造成不必要的開銷,所以者這種不建議使用。

DCL單例(雙重檢查鎖定)
public class Singleton {
    private volatile static Singleton singleton;

    private Singleton(){
    }

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

這種寫法在getSingleton方法中對singleton進行了兩次判空,第一次是為了不必要的同步,第二次是為了在null的情況下創(chuàng)建實例。我們會發(fā)現(xiàn)上面代碼有一個volatile關(guān)鍵字,因為在這里會有DCL失效問題,原因是Java編譯器允許處理器亂序執(zhí)行。那么為了解決這個問題,在JDK1.5之后,具體化了volatile關(guān)鍵字,只要定義時加上他,可以保證執(zhí)行的順序,雖然會影響性能。這種方式第一次加載時會稍慢,在高并發(fā)環(huán)境會有缺陷,但是一般能夠滿足需求。

靜態(tài)內(nèi)部類單例模式
public class Singleton {
    private Singleton(){
    }

    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    /**
    *靜態(tài)內(nèi)部類
    */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

這種是推薦使用的單例模式實現(xiàn)方式。當?shù)谝淮渭虞dSingleton類時并不會初始化INSTANCE,只有在第一次調(diào)用getInstance方法時才會導(dǎo)致INSTANCE被初始化。這種方式不僅能夠保證線程安全,也能保證單例對象的唯一性,同時也延長了單例的實例化。

使用容器實現(xiàn)單例模式
public class SingletonManager {
    private static Map<String, Object> objMap = new HashMap<String,Object>();

    private Singleton() { 
    }

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

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

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

總結(jié)

單例模式是運用頻率很高的模式,但是,由于在客戶端通常沒有高并發(fā)的情況,因此,選擇哪種實現(xiàn)方式都不會有太大的影響。即使如此,出于效率考慮,推薦使用DCL單例(雙重檢查鎖定)和靜態(tài)內(nèi)部類單例模式。

  • 優(yōu)點:

    • 由于單例模式在內(nèi)存中只有一個實例,減少了內(nèi)存開支,特別是一個對象需要頻繁的創(chuàng)建、銷毀時,而且創(chuàng)建或銷毀時性能又無法優(yōu)化,單例模式的優(yōu)勢就非常明顯。
    • 單例模式可以避免對資源的多重占用,例如一個文件操作,由于只有一個實例存在內(nèi)存中,避免對同一資源文件的同時操作。
    • 單例模式可以在系統(tǒng)設(shè)置全局的訪問點,優(yōu)化和共享資源訪問,例如,可以設(shè)計一個單例類,負責所有數(shù)據(jù)表的映射處理。
  • 缺點:

    • 單例模式一般沒有接口,擴展很困難,若要擴展,只能修改代碼來實現(xiàn)。
    • 單例對象如果持有Context,那么很容易引發(fā)內(nèi)存泄露。此時需要注意傳遞給單例對象的Context最好是Application Context。

更多內(nèi)容戳這里(整理好的各種文集)

最后編輯于
?著作權(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)容