java單例設(shè)計(jì)模式以及在android上面的應(yīng)用

關(guān)于java的單例設(shè)計(jì)模式,是項(xiàng)目當(dāng)中很常用的設(shè)計(jì)模式。當(dāng)某個(gè)資源,或者某個(gè)實(shí)例 ,整個(gè)項(xiàng)目只使用一份的情況下,我們就需要用這個(gè)去創(chuàng)建一個(gè)單例,例如工具類等,都是要用到這種模式的。項(xiàng)目中對(duì)于這些工具類,往往只需要維持一個(gè)對(duì)象,然后一直用這個(gè)對(duì)象就可以了,以此減少內(nèi)存的開支。

class SingleTonClass {
    //餓漢式單例模式 提前創(chuàng)建對(duì)象
    private static SingleTonClass singleTonClass = new SingleTonClass();

    private SingleTonClass() {
    }

    public static SingleTonClass getInstance() {
        return singleTonClass;
    }
}
class SimpleClass {
  //懶漢式單例模式 等到需求時(shí)再去判斷是否需要?jiǎng)?chuàng)建對(duì)象
    private static SimpleClass simpleClass;
    private SimpleClass() {
    }
    public static SimpleClass getInstance() {
        if (simpleClass == null) {//懶漢式 
            simpleClass = new SimpleClass();
        }
        return simpleClass;
    }
}

上述寫法,如果在線程并發(fā)執(zhí)行的狀態(tài)下,很可能會(huì)出現(xiàn)多個(gè)實(shí)例被創(chuàng)建。
所以我們可以改造getInstance()方法,去適應(yīng)多線程情況。

  public static SimpleClass getInstance() {
        if (simpleClass == null) {
            synchronized (SimpleClass.class) {//減小同步區(qū)域 更好的優(yōu)化性能
                if (simpleClass == null) {
                    simpleClass = new SimpleClass();
                }
            }
        }
        return simpleClass;
    }

雙重鎖定使得這個(gè)函數(shù)更加的安全,但是依然有問題
因?yàn)閟impleClass = new SimpleClass();這段代碼 ,并不是原子性操作,他包含好幾個(gè)步驟:

  1. 為對(duì)象分配內(nèi)存
  2. 初始化實(shí)例對(duì)象
  3. 把引用instance指向分配的內(nèi)存空間

這個(gè)三個(gè)步驟并不能保證按序執(zhí)行,處理器會(huì)進(jìn)行指令重排序優(yōu)化,存在這樣的情況:

優(yōu)化重排后執(zhí)行順序?yàn)椋?,3,2, 這樣在線程1執(zhí)行到3時(shí),instance已經(jīng)不為null了,線程2此時(shí)判斷instance!=null,則直接返回instance引用,但現(xiàn)在實(shí)例對(duì)象還沒有初始化完畢,此時(shí)線程2使用instance可能會(huì)造成程序崩潰。
現(xiàn)在要解決的問題就是怎樣限制處理器進(jìn)行指令優(yōu)化重排。
答案就是用volatile關(guān)鍵字 ,他提供了線程之間修改可見性。

class SimpleClass {
    private static volatile SimpleClass simpleClass;
    private SimpleClass() {
    }
    public static SimpleClass getInstance() {
        if (simpleClass == null) {
            synchronized (SimpleClass.class) {
                if (simpleClass == null) {
                    simpleClass = new SimpleClass();
                }
            }
        }
        return simpleClass;
    }
}

解釋一下volatile關(guān)鍵字:

1.保證可見性

可以保證在多線程環(huán)境下,變量的修改可見性。每個(gè)線程都會(huì)在工作內(nèi)存(類似于寄存器和高速緩存),實(shí)例對(duì)象都存放在主內(nèi)存中,在每個(gè)線程要使用的時(shí)候把主內(nèi)存中的內(nèi)容拷貝到線程的工作內(nèi)存中。使用volatile關(guān)鍵字修飾后的變量,保證每次修改了變量需要立即寫回主內(nèi)存中,同時(shí)通知所有的該對(duì)變量的緩存失效,保證緩存一致性,其他線程需要使用該共享變量時(shí)就要重新從住內(nèi)存中獲取最新的內(nèi)容拷貝到工作內(nèi)存中供處理器使用。這樣就可以保證變量修改的可見性了。但volatile不能保證原子性,比如++操作。

2.提供內(nèi)存屏障

volatile關(guān)鍵字能夠通過提供內(nèi)存屏障,來保證某些指令順序處理器不能夠優(yōu)化重排,編譯器在生成字節(jié)碼時(shí),會(huì)在指令序列中插入內(nèi)存屏障來禁止特定類型的處理器重排序。

下面是保守策略插入內(nèi)存屏障:

    在每個(gè)volatile寫操作的前面插入一個(gè)StoreStore屏障。

    在每個(gè)volatile寫操作的后面插入一個(gè)StoreLoad屏障。

    在每個(gè)volatile讀操作的前面插入一個(gè)LoadLoad屏障。

    在每個(gè)volatile讀操作的后面插入一個(gè)LoadLoad屏障。

這樣可以保證在volatile關(guān)鍵字修飾的變量的賦值和讀取操作前后兩邊的大的順序不會(huì)改變,
在內(nèi)存屏障前面的順序可以交換,屏障后面的也可以換序,但是不能跨越內(nèi)存屏障重排執(zhí)行順序。
好了,現(xiàn)在來看上面的單例模式,這樣就可以保證3步驟(instance賦值操作)是保持最后一步完成,
這樣就不會(huì)出現(xiàn)instance在對(duì)象沒有初始化時(shí)就不為null的情況了。這樣也就實(shí)現(xiàn)了正確的單例模式了。

還有一種枚舉,也是單例模式的實(shí)現(xiàn):

    public enum SingleEnum{  
        INSTANCE;  //不建議用 
    }  

關(guān)于單例模式在android方面的應(yīng)用:
單例模式一般運(yùn)用在一些工具類上面。還有,界面之間傳輸數(shù)據(jù),有可能會(huì)因?yàn)閿?shù)據(jù)量太大,造成TransactionTooLargeException異常的,所以這個(gè)時(shí)候,就需要通過一個(gè)幫助類去存儲(chǔ)這段數(shù)據(jù),為了保證數(shù)據(jù)的正確性,這個(gè)時(shí)候必定就是要用到單例模式的。

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 一個(gè)簡單的單例示例 單例模式可能是大家經(jīng)常接觸和使用的一個(gè)設(shè)計(jì)模式,你可能會(huì)這么寫 publicclassUnsa...
    Martin說閱讀 2,405評(píng)論 0 6
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,883評(píng)論 11 349
  • 1.單例模式概述 (1)引言 單例模式是應(yīng)用最廣的模式之一,也是23種設(shè)計(jì)模式中最基本的一個(gè)。本文旨在總結(jié)通過Ja...
    曹豐斌閱讀 3,078評(píng)論 6 47
  • 【提要】: 春秋時(shí)期,有宋人司馬牛多言而躁,嘗問仁及君子于孔子。 一日,司馬牛憂曰:人皆有兄弟,我獨(dú)亡! 子夏曰:...
    春江水暖閱讀 6,719評(píng)論 1 1
  • 我已經(jīng)忘了你說這些話時(shí)的表情,忘不掉的,不過是不甘心罷了.
    青戈漫嫵閱讀 166評(píng)論 0 1

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