溫馨提示×

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

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

實(shí)現(xiàn)singleton的四種方法

發(fā)布時(shí)間:2020-05-15 11:33:29 來(lái)源:億速云 閱讀:192 作者:Leah 欄目:編程語(yǔ)言

這篇文章主要為大家詳細(xì)介紹了實(shí)現(xiàn)singleton的四種方法,文中示例代碼介紹的非常詳細(xì),零基礎(chǔ)也能參考此文章,感興趣的小伙伴們可以參考一下。

消耗內(nèi)存最嚴(yán)重的對(duì)象創(chuàng)建過(guò)程,必須對(duì)其進(jìn)行約束,作為創(chuàng)建型模式的單例模式(Singleton),始終保持應(yīng)用程序中某一個(gè)實(shí)例有且僅有一個(gè),可以很顯著的提升程序性能。

 單線程下的Singleton的穩(wěn)定性是極好的,可分為兩大類:

1.Eager(餓漢型): 類加載時(shí)立即創(chuàng)建對(duì)象。

public class EagerSingleton {
    //1. 類加載時(shí)就立即產(chǎn)生實(shí)例對(duì)象,通過(guò)設(shè)置靜態(tài)變量被外界獲取
    //2. 并使用private保證封裝安全性
    private static EagerSingleton eagerSingleton  = new EagerSingleton();
    
    //3. 通過(guò)構(gòu)造方法的私有化,不允許外部直接創(chuàng)建對(duì)象,確保單例的安全性
    private EagerSingleton(){
    }
    public static EagerSingleton getEagerSingleton(){
        return eagerSingleton;
    }

2.Lazy(懶漢型):類加載時(shí)沒有立即創(chuàng)建對(duì)象,等到第一個(gè)用戶獲取才進(jìn)行實(shí)例化。

public class LazySingleton {
    //1. 類加載時(shí)并沒有創(chuàng)建唯一實(shí)例
    private static LazySingleton lazySingleton;
    
    private LazySingleton() {
    }
        
    //2、提供一個(gè)獲取實(shí)例的靜態(tài)方法
    public static LazySingleton getLazySingleton() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        } 
        return lazySingleton;
    }

就性能方面而言,LazySingleton 明顯優(yōu)于 EagerSingleton ,若類的加載需要耗費(fèi)大量的資源(e.g. 讀取大文件信息),那么LazySingleton 的優(yōu)勢(shì)顯而易見。但通過(guò)閱讀代碼,很容易發(fā)現(xiàn)一個(gè)致命問(wèn)題。多線程間如何保持安全性?

下面將對(duì)多線程并發(fā)問(wèn)題進(jìn)行解析:

解決該問(wèn)題的關(guān)鍵在于兩方面:1.同步;  2.性能;

如何解救呢?
作為一個(gè)java的開發(fā)者,對(duì)synchronized一定不陌生,提到多線程,大部分人想到的都是他(JDK6后,他的性能提升巨大,解決簡(jiǎn)單并發(fā),非常適用)。

那就讓我們用synchronized來(lái)嘗試解決吧:

//由synchronized進(jìn)行同步加鎖
public synchronized static LazySingleton getLazySingleton() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        } 
        return lazySingleton;
    }

如此同步問(wèn)題看似解決,但是作為一個(gè)開發(fā)者,最重要的是性能的保障,使用synchronized有利有弊,由于加鎖操作,代碼段被加上悲觀鎖,只有等一個(gè)請(qǐng)求完成,下個(gè)請(qǐng)求才能進(jìn)入執(zhí)行。通常加上synchronized關(guān)鍵字的代碼片會(huì)比同等量級(jí)的代碼慢上幾倍,這是我們不愿見到的。那如何避免這一問(wèn)題呢?在java對(duì)synchronized的定義里有這樣的建議:越遲使用synchronized,性能越優(yōu)(細(xì)化鎖)。

###### 2.因此,我們需要開始解決性能的問(wèn)題了。按照synchronized優(yōu)化: ######

public class DoubleCheckLockSingleton {
    //使用volatile保證每次取值不是從緩存中取,而是從真正對(duì)應(yīng)的內(nèi)存地址中取.(下文解釋)
    private static volatile DoubleCheckLockSingleton doubleCheckLockSingleton;
    
    private DoubleCheckLockSingleton(){
        
    }
    
    public static DoubleCheckLockSingleton getDoubleCheckLockSingleton(){
        //配置雙重檢查鎖(下文解釋)
        if(doubleCheckLockSingleton == null){
            synchronized (DoubleCheckLockSingleton.class) {
                if(doubleCheckLockSingleton == null){
                    doubleCheckLockSingleton = new DoubleCheckLockSingleton();
                }
            }
        }
        return doubleCheckLockSingleton;
    }
}

上述源碼就是經(jīng)典的volatile關(guān)鍵字(JDK1.5 后重生)+雙重檢查鎖(DoubleCheck),最大程度的優(yōu)化了sychronized帶來(lái)的性能開銷。下面將為大家解釋volatile與DoubleCheck。

1.volatile

是在JDK1.5后才正式被實(shí)現(xiàn)使用的,之前的版本只是定義了該關(guān)鍵字,未有具體實(shí)現(xiàn)。若想理解volatile就必須對(duì)JVM自身的內(nèi)存管理有些許了解:

1.1 遵循著摩爾定律,內(nèi)存的讀寫速度已遠(yuǎn)不能滿足CPU,因此現(xiàn)代計(jì)算機(jī)引入了在CPU上添加高速緩存的機(jī)制,由緩存預(yù)讀取內(nèi)存的值,并暫存于緩存中,通過(guò)計(jì)算,再更新內(nèi)存中的相應(yīng)值。

**1.2** 而JVM模仿PC的這一做法,在內(nèi)存中劃分了自己的**工作內(nèi)存**,該部分內(nèi)存作用與高速緩存一致,很顯著的提高JVM工作效率,但凡事都有利有弊,這一做法也導(dǎo)致工作內(nèi)存與其他內(nèi)存通信時(shí)容易導(dǎo)致傳輸上的問(wèn)題。volatile的一個(gè)功能就是強(qiáng)制的從內(nèi)存中讀取最新的值,避免緩存與內(nèi)存不一致的狀況。

1.3 volatile的另一個(gè)功能也是和JVM相關(guān),即JVM會(huì)通過(guò)自身的判斷,將源碼的執(zhí)行順序重排,保證指令流水線連貫性,以達(dá)到最優(yōu)的執(zhí)行方案。這種做法提高了性能,但對(duì)DoubleCheck卻會(huì)產(chǎn)生意想外的結(jié)果,兩線程可能互相干擾。而volatile提供了happens-before guarantee(寫優(yōu)先于讀),使對(duì)象不被干擾,保證安全的穩(wěn)定性。

2.DoubleCheck

這是現(xiàn)代編程的遺留,假設(shè)進(jìn)入同步塊之后,對(duì)象已被實(shí)例化,此時(shí)需再次進(jìn)行判斷。

當(dāng)然還有一種官方推薦的單例實(shí)現(xiàn)方法

由于類的構(gòu)造在定義中已是原子性的,因此上述的各種問(wèn)題都不會(huì)再產(chǎn)生,是一種很好的單例實(shí)現(xiàn)方式,推薦使用。

//使用內(nèi)部類進(jìn)行單例構(gòu)造
public class NestedClassSingleton {
    private NestedClassSingleton(){
        
    }
    private static class SingletonHolder{
        private static final NestedClassSingleton nestedClassSingleton = new NestedClassSingleton();
    }
    public static NestedClassSingleton getNestedClassSingleton(){
        return SingletonHolder.nestedClassSingleton;
    }
}

看完上述內(nèi)容,你們掌握實(shí)現(xiàn)singleton的四種方法的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

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

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

AI