設(shè)計(jì)模式 <單例模式>

定義:

保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。

1. 餓漢模式

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

    private Singleton() {
    }

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

這種方式在類加載時(shí)就完成了初始化,所以類加載較慢,但獲取對(duì)象的速度快。 這種方式基于類加載機(jī)制避免了多線程的同步問題。

2. 懶漢模式(線程不安全)

public class Singleton {
    private static Singleton instance;

    private Singleton() {
    }

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

        return instance;
    }
}

懶漢模式申明了一個(gè)靜態(tài)對(duì)象,在用戶第一次調(diào)用時(shí)初始化,雖然節(jié)約了資源,但第一次加載時(shí)需要實(shí)例化,反映稍慢一些,而且在多線程不能正常工作。

3. 懶漢模式(線程安全)

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方法時(shí)都需要進(jìn)行同步,造成不必要的同步開銷,而且大部分時(shí)候我們是用不到同步的,所以不建議用這種模式。

4. 雙重檢查模式 (DCL)

public class Singleton {
    private volatile static Singleton singleton;

    private Singleton() {
    }

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

        return singleton;
    }
}

這種寫法在getSingleton方法中對(duì)singleton進(jìn)行了兩次判空,第一次是為了不必要的同步,第二次是在singleton等于null的情況下才創(chuàng)建實(shí)例。
在這里使用volatile會(huì)或多或少的影響性能,但考慮到程序的正確性,犧牲這點(diǎn)性能還是值得的。 DCL優(yōu)點(diǎn)是資源利用率高,第一次執(zhí)行g(shù)etInstance時(shí)單例對(duì)象才被實(shí)例化,效率高。缺點(diǎn)是第一次加載時(shí)反應(yīng)稍慢一些,在高并發(fā)環(huán)境下也有一定的缺陷,雖然發(fā)生的概率很小。DCL雖然在一定程度解決了資源的消耗和多余的同步,線程安全等問題,但是他還是在某些情況會(huì)出現(xiàn)失效的問題,也就是DCL失效,在《java并發(fā)編程實(shí)踐》一書建議用靜態(tài)內(nèi)部類單例模式來替代DCL。

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

public class Singleton {
    private Singleton() {
    }

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

    private static class SingletonHolder {
        private static final Singleton sInstance = new Singleton();
    }
}

第一次加載Singleton類時(shí)并不會(huì)初始化sInstance,只有第一次調(diào)用getInstance方法時(shí)虛擬機(jī)加載SingletonHolder 并初始化sInstance ,這樣不僅能確保線程安全也能保證Singleton類的唯一性,所以推薦使用靜態(tài)內(nèi)部類單例模式。

6. 枚舉單例

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

默認(rèn)枚舉實(shí)例的創(chuàng)建是線程安全的,并且在任何情況下都是單例,上述講的幾種單例模式實(shí)現(xiàn)中,有一種情況下他們會(huì)重新創(chuàng)建對(duì)象,那就是反序列化,將一個(gè)單例實(shí)例對(duì)象寫到磁盤再讀回來,從而獲得了一個(gè)實(shí)例。反序列化操作提供了readResolve方法,這個(gè)方法可以讓開發(fā)人員控制對(duì)象的反序列化。在上述的幾個(gè)方法示例中如果要杜絕單例對(duì)象被反序列化是重新生成對(duì)象,就必須加入如下方法:

private Object readResolve() throws ObjectStreamException{
    return singleton;
}

枚舉單例的優(yōu)點(diǎn)就是簡(jiǎn)單,但是大部分應(yīng)用開發(fā)很少用枚舉,可讀性并不是很高,不建議用。

7. 使用容器實(shí)現(xiàn)單例模式

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

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

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

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

總結(jié)

到這里七種寫法都介紹完了,至于選擇用哪種形式的單例模式,取決于你的項(xiàng)目本身,是否是有復(fù)雜的并發(fā)環(huán)境,還是需要控制單例對(duì)象的資源消耗。

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

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