溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

折騰Java設(shè)計模式之單例模式

發(fā)布時間:2020-07-12 11:53:51 來源:網(wǎng)絡(luò) 閱讀:304 作者:大萌小路 欄目:軟件技術(shù)

博文原址:折騰Java設(shè)計模式之單例模式

單例模式

Ensure a class has only one instance, and provide a global point of access to it.

一個類僅僅只有一個實例,并且提供全局的接入點。簡潔點理解就是涉及到一個單一的類,該類負(fù)責(zé)創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建。這個類提供了一種訪問它自己唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。

餓漢式單例模式

public final class EagerSingleton {

    private static final EagerSingleton INSTANCE = new EagerSingleton();

    private EagerSingleton() {
    }

    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

基于 classloader 機(jī)制避免了多線程的同步問題,不過INSTANCE在類裝載時就實例化,雖然導(dǎo)致類裝載的原因有很多種,在單例模式中大多數(shù)都是調(diào)用 getInstance 方法。類在加載時就初始化了,會浪費空間,因為不管你用還是不用,它都創(chuàng)建出來了,但是因為沒有加鎖,執(zhí)行效率較高。

懶漢式單例模式

/**
 * 懶漢式的單例模式-線程不安全的
 */
public final class LazyThreadNotSafeSingleton {

    private static LazyThreadNotSafeSingleton INSTANCE;

    private LazyThreadNotSafeSingleton() {
    }

    public static LazyThreadNotSafeSingleton getInstance() {
        if (null == INSTANCE) {
            INSTANCE = new LazyThreadNotSafeSingleton();
        }
        return INSTANCE;
    }
}
/**
 * 懶漢式的單例模式-線程安全的
 */
public final class LazyThreadSafeSingleton {

    private static LazyThreadSafeSingleton INSTANCE;

    private LazyThreadSafeSingleton() {
    }

    public static synchronized LazyThreadSafeSingleton getInstance() {
        if (null == INSTANCE) {
            INSTANCE = new LazyThreadSafeSingleton();
        }
        return INSTANCE;
    }
}

有上述兩種懶漢式單例模式,區(qū)別在與靜態(tài)工廠方法getInstance是否加了synchronized修飾來進(jìn)行同步,用于支持線程安全。懶漢式,在其加載對象的時候是不會創(chuàng)建對象實例的,只有等它真正使用的時候才會創(chuàng)建,如果一直沒有使用則一直不會創(chuàng)建,能夠避免內(nèi)存浪費,也就是只有第一次調(diào)用的時候才會創(chuàng)建。但是加鎖synchronized就影響了性能和效率,導(dǎo)致getInstance方法的性能受影響,此種方式也不推薦。尋找一種既能線程安全又可以延遲加載的方式。

雙檢查鎖的單例模式

/**
 * 雙檢查鎖的單例模式-線程安全
 */
public final class DoubleCheckLockingSingleton {

    private static volatile DoubleCheckLockingSingleton INSTANCE;

    private DoubleCheckLockingSingleton() {
    }

    public static DoubleCheckLockingSingleton getInstance() {
        // 第一次檢查實例是否存在,如果存在即可返回,不存在則進(jìn)入同步塊
        if (null == INSTANCE) {
            // 同步塊,線程安全
            synchronized (DoubleCheckLockingSingleton.class) {
                // 第二次檢查實例是否存在,如果還不存在則會真正的創(chuàng)建實例
                if (null == INSTANCE) {
                    INSTANCE = new DoubleCheckLockingSingleton();
                }
            }
        }
        return INSTANCE;
    }
}

雙檢查加鎖的方式,能實現(xiàn)線程安全,又能減少性能的影響。雙檢查加鎖,旨在每次調(diào)用getInstance方法都需要同步,但是先不會同步,在第一次判斷實例是否存在后,如果不存在才進(jìn)入同步塊,進(jìn)入同步塊后,第二次檢查實例是否存在,如果不存在,在同步塊內(nèi)創(chuàng)建實例。如此只有首次才會同步,從而減少了多次在同步情況下進(jìn)行判斷所浪費的時間。雙檢查加鎖機(jī)制的實現(xiàn)會使用關(guān)鍵字volatile,它的意思是:被volatile修飾的變量的值,將不會被本地線程緩存,所有對該變量的讀寫都是直接操作共享內(nèi)存,從而確保多個線程能正確的處理該變量。但是實現(xiàn)過程稍微復(fù)雜點。

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

/**
 * 靜態(tài)內(nèi)部類Holder式單例
 *
 * 延遲加載和線程安全
 */
public final class LazyInitializationHolderSingleton {

    private LazyInitializationHolderSingleton() {
    }

    public static LazyInitializationHolderSingleton getInstance() {
        return InstanceHolder.INSTANCE;
    }

    /**
     * 延遲加載
     */
    private static class InstanceHolder {

        private static final LazyInitializationHolderSingleton INSTANCE = new LazyInitializationHolderSingleton();
    }
}

當(dāng)getInstance方法第一次被調(diào)用的時候,它第一次讀取InstanceHolder.INSTANCE,導(dǎo)致InstanceHolder類得到初始化;而這個類在裝載并被初始化的時候,會初始化它的靜態(tài)域,從而創(chuàng)建Singleton的實例,由于是靜態(tài)的域,因此只會在虛擬機(jī)裝載類的時候初始化一次,并由虛擬機(jī)來保證它的線程安全性。這個模式的優(yōu)勢在于,getInstance方法并沒有被同步,并且只是執(zhí)行一個域的訪問,因此延遲初始化并沒有增加任何訪問成本。其中使用到類的靜態(tài)內(nèi)部類和多線程缺省同步鎖。

靜態(tài)內(nèi)部類

靜態(tài)內(nèi)部類指,有static修飾的成員式內(nèi)部類。如果沒有static修飾的成員式內(nèi)部類被稱為對象級內(nèi)部類。類級內(nèi)部類相當(dāng)于其外部類的static成分,它的對象與外部類對象間不存在依賴關(guān)系,因此可直接創(chuàng)建。而對象級內(nèi)部類的實例,是綁定在外部對象實例中的。靜態(tài)內(nèi)部類中,可以定義靜態(tài)的方法。在靜態(tài)方法中只能夠引用外部類中的靜態(tài)成員方法或者成員變量。靜態(tài)內(nèi)部類相當(dāng)于其外部類的成員,只有在第一次被使用的時候才被會裝載。

多線程缺省同步鎖

在多線程開發(fā)中,為解決并發(fā)問題,主要是通過使用synchronized來加互斥鎖進(jìn)行同步控制。但是在某些情況中,JVM已經(jīng)隱含地為您執(zhí)行了同步,這些情況下就不用自己再來進(jìn)行同步控制了。這些情況包括:

1.由靜態(tài)初始化器(在靜態(tài)字段上或static{}塊中的初始化器)初始化數(shù)據(jù)時;

2.訪問final字段時;

3.在創(chuàng)建線程之前創(chuàng)建對象時;

4.線程可以看見它將要處理的對象時

枚舉類型的單例模式

/**
 * 采用枚舉類型的單例模式
 */
public enum SingletonEnum {

    INSTANCE;

    @Override
    public String toString() {
        return getDeclaringClass().getCanonicalName() + "@" + hashCode();
    }

    public void something(){
        //do something...
    }

}

簡潔,自動支持序列化機(jī)制,絕對防止多次實例化?!陡咝ava 第二版》中的說法:單元素的枚舉類型已經(jīng)成為實現(xiàn)Singleton的最佳方法。用枚舉來實現(xiàn)單例非常簡單,只需要編寫一個包含單個元素的枚舉類型即可。

總結(jié)

不建議使用懶漢式,簡單的闊以使用餓漢式。涉及到反序列化創(chuàng)建對象時闊以使用枚舉方式。如果考慮到延遲加載 的話,闊以采用靜態(tài)內(nèi)部類Holder的模式。如果對業(yè)務(wù)需求有特殊要求的時候闊以采用雙檢查鎖的單例。

參考

源碼地址

單例模式|菜鳥教程

《JAVA與模式》之單例模式

歡迎關(guān)注了解最新動態(tài)更新

折騰Java設(shè)計模式之單例模式cdn.xitu.io/2019/1/25/168827caf090df03?w=258&h=258&f=jpeg&s=19969">

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI