設(shè)計(jì)模式讀書筆記之單例模式

單例模式是確保一個類只有實(shí)例,而且自行實(shí)例化并向整個系統(tǒng)提供實(shí)例。它可能是應(yīng)用使用最廣泛的模式,也是最廣為人知的一種設(shè)計(jì)模式,就連我這個菜逼提到單例模式也能說上兩句,蹦出懶漢模式,餓漢模式這樣的名詞。

那它究竟是怎么樣的,又有多少種呢,又是在怎樣的場景中使用呢?
我們先來看看它的使用場景,如上所說,它是確保一個類有且只有一個實(shí)例對象,避免產(chǎn)生多個對象消耗過多資源或某種對象只應(yīng)該有且只有一個的場景,比如訪問io或數(shù)據(jù)庫的這樣比較消耗的對象。

角色介紹:
(1)Client--高層客戶端

(2)SingLecton--單例類
實(shí)現(xiàn)單列模式的關(guān)鍵點(diǎn):
1.構(gòu)造函數(shù)不對外開放,用private來修飾
2.通過一個靜態(tài)方法或枚舉來返回對象
3.確保單例對象有且只有一個,尤其是在多線程情況下
4.確保單例對象不會在反序列化里重構(gòu)對象。
通過單例類的構(gòu)造函數(shù)私有化使得客戶端不能通過new的形式來手動構(gòu)造單例類的對象,只能通過單例對象暴露的靜態(tài)方法獲取到單例對象的唯一對象,同時在獲取到這個唯一的對象時候也要保證線程的安全,即在多線程環(huán)境下也要保證構(gòu)造的單例對象有且只有一個,這也是單例模式里實(shí)現(xiàn)比較困難的地方。
下面用代碼來看看幾種單例模式的表現(xiàn)形式吧。
1.餓漢模式

public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton(){

    }

    public static Singleton getInstance(){
        return instance;
    }

餓漢模式將對象構(gòu)造函數(shù)私有化,不能通過new獲取,而我們將對象設(shè)為靜態(tài),并在聲明的時候初始化。只能通過靜態(tài)方法獲取,保證了對象的唯一性。
2.懶漢模式(線程不安全,不推薦)

public class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

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

該模式添加了關(guān)鍵字synchronized說明是同步方法,保證了在多線程模式下對象唯一的手段。問題在于,第一次調(diào)用初始化后
每次調(diào)用getInstance()方法仍會進(jìn)行同步,會消耗不必要資源,一般不推薦
總結(jié)懶漢模式下優(yōu)點(diǎn)在于只有被調(diào)用的時候才會去實(shí)例化,在一定程度上節(jié)約了資源,缺點(diǎn)是第一次調(diào)用加載及時實(shí)例化,反應(yīng)稍慢,每次調(diào)用的
時候會同步,增加不必要的同步開銷。
3.DCL模式

public class Singleton {
    private static Singleton instance=null;

    private Singleton() {

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

程序亮點(diǎn)在于getInstance方法上,在此方法里對instance進(jìn)行兩次判空,第一次是為了避免不必要的同步,第二次判空 是為了在null的情況下創(chuàng)建實(shí)例。解決了資源消耗,多余同步,線程安全問題。
更詳細(xì)的解釋:
假設(shè)線程A執(zhí)行到 instance=new Singleton()的時候,它大致做了三件事情:
給Singleton實(shí)例分配內(nèi)存,
調(diào)用構(gòu)造函數(shù),初始化字段
將instance對象指向分配的內(nèi)存空間(此時Singlecton已經(jīng)不為null)

DCL的優(yōu)點(diǎn):資源利用率高,效率高
缺點(diǎn):第一次加載反應(yīng)稍慢,由于java內(nèi)存模型的原因偶爾會失敗,在高并發(fā)環(huán)境下有一定缺陷(概率很?。?。DCL是單例使用最多的模式。
4.靜態(tài)內(nèi)部內(nèi)模式

public class Singleton {


    private Singleton() {

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

    public static class SingletonHoulder{
        private static final Singleton instance = new Singleton();
    }

靜態(tài)內(nèi)部內(nèi)方式靜態(tài)內(nèi)部內(nèi)模式,是為了防止dcl模式在某些情況下失效(雙重鎖定失效)而產(chǎn)生出來的。
5.枚舉模式

public enum Singleton {  
     INSTANCE;  
     public void whateverMethod() {  
     }  
 }  

這種方式是Effective Java作者Josh Bloch 提倡的方式,它不僅能避免多線程同步問題,而且還能防止反序列化重新創(chuàng)建新的對象。
6.容器模式

public class SinglectonManager {
    private static Map<String,Object> objectMap = new HashMap<String, Object>();
    private SinglectonManager(){
    }
    private static void resgisterService(String key,Object instance){
        if (!objectMap.containsKey(key)){
            objectMap.put(key,instance);
        }
    }
    public static Object getService(String key){
       return objectMap.get(key);
    }
}

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

總結(jié):
優(yōu)點(diǎn):
1.單例模式在內(nèi)存中只有一個實(shí)例,減少了內(nèi)存的開支,尤其是當(dāng)一個對象頻繁創(chuàng)建銷毀,而創(chuàng)建銷毀時性能無法優(yōu)化優(yōu)勢最為明顯
2.減少系統(tǒng)性能開銷,當(dāng)一個對象產(chǎn)生需要較多資源時候,讀取配置,依賴對象時候,可以使其直接產(chǎn)生一個單例,然后永久駐留內(nèi)存的方式解決。
3.避免對資源文件的多重占用
4.優(yōu)化和共享資源訪問

缺點(diǎn):
1.沒有接口,擴(kuò)展困難
2.如果持有Context,容易引發(fā)內(nèi)存泄露(最好是ApplicationContext)

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

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

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