Android 單例模式的正確姿勢

單例模式是使用得最多的設(shè)計(jì)模式俗冻,模版代碼也很多梨水。但是如果使用不當(dāng)還是容易出問題乏矾。

DCL模式(雙重檢查鎖定模式)的正確使用方式

一般我們使用DCL方法來實(shí)現(xiàn)單例模式時(shí)都是這樣的模版代碼:

private static Singleton mSingleton = null;
private Singleton () {}

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

}

實(shí)際上屋剑,上述方法在多線程的環(huán)境下润匙,還是會(huì)有可能創(chuàng)建多個(gè)實(shí)例。為什么呢唉匾?

mSingleton = new Singleton()這行代碼虛擬機(jī)在執(zhí)行的時(shí)候會(huì)有多個(gè)操作孕讳,大致包括:

  • 為新的對象分配內(nèi)存
  • 調(diào)用Singleton的構(gòu)造方法,初始化成員變量
  • 將mSingleton這個(gè)引用指向新創(chuàng)建的Singleton對象的地址

在多線程環(huán)境下肄鸽,每個(gè)線程的私有內(nèi)存空間中都有mSingleton的副本卫病。這導(dǎo)致可能存在下面的情況:

  • 當(dāng)在一個(gè)線程中初始化mSingleton后,主內(nèi)存中的mSingleton變量的值可能并沒有及時(shí)更新典徘;
  • 主內(nèi)存的mSingleton變量已經(jīng)更新了蟀苛,但在另一個(gè)線程中的mSingleton變量沒有即時(shí)從主內(nèi)存中讀取最新的值

這樣的話就有可能創(chuàng)建多個(gè)實(shí)例,雖然這種幾率比較小逮诲。

那怎么解決這個(gè)問題呢帜平?答案是使用volatile關(guān)鍵字

volatile關(guān)鍵字能夠保證可見性,被volatile修飾的變量梅鹦,在一個(gè)線程中被改變時(shí)會(huì)立刻同步到主內(nèi)存中裆甩,而另一個(gè)線程在操作這個(gè)變量時(shí)都會(huì)先從主內(nèi)存更新這個(gè)變量的值。

更保險(xiǎn)的單例模式實(shí)現(xiàn)

private volatile static Singleton mSingleton = null;
private Singleton () {}

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

}

使用單例模式齐唆,小心內(nèi)存泄漏了喔~

單例模式的靜態(tài)特性導(dǎo)致它的對象的生命周期是和應(yīng)用一樣的嗤栓,如果不注意這一點(diǎn)就可能導(dǎo)致內(nèi)存泄漏。下面看看常見的2種情況

  • Context的泄漏
//SingleInstance.class
private volatile static SingleInstance mSingleInstance = null;
private SingleInstance (Context context) {}

public static SingleInstance getInstance(Context context) {
    if (mSingleInstance == null) {
        synchronized (SingleInstance.class) {
            if (mSingleInstance == null) {
                mSingleInstance = new SingleInstance(context);
            }
        }
    }
    return mSingleInstance;

}

//MyActivity
public class MyActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //這樣就容易出問題了
        SingleInstance singleInstance = SingleInstance.getInstance(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}

如上面那樣直接傳入MyActivity的引用,如果當(dāng)前MyActivity退出了茉帅,但應(yīng)用還沒有退出叨叙,singleInstance一直持有MyActivity的引用,MyActivity就不能被回收了堪澎。

解決方法也很簡單擂错,傳入ApplicationContext就可以了。

SingleInstance singleInstance = SingleInstance.getInstance(getApplicationContext());
  • View的泄漏

如果單例模式的類中有跟View相關(guān)的屬性樱蛤,就需要注意了钮呀。搞不好也會(huì)導(dǎo)致內(nèi)存泄漏,原因和上面分析的原因一樣昨凡。

//SingleInstance.class
private volatile static SingleInstance mSingleInstance = null;
private SingleInstance (Context context) {}

public static SingleInstance getInstance(Context context) {
    if (mSingleInstance == null) {
        synchronized (SingleInstance.class) {
            if (mSingleInstance == null) {
                mSingleInstance = new SingleInstance(context);
            }
        }
    }
    return mSingleInstance;

}

//單例模式中這樣持有View的引用會(huì)導(dǎo)致內(nèi)存泄漏
private View myView = null;
public void setMyView(View myView) {
    this.myView = myView;
}

解決方案是采用弱引用

private volatile static SingleInstance mSingleInstance = null;
private SingleInstance (Context context) {}

public static SingleInstance getInstance(Context context) {
    if (mSingleInstance == null) {
        synchronized (SingleInstance.class) {
            if (mSingleInstance == null) {
                mSingleInstance = new SingleInstance(context);
            }
        }
    }
    return mSingleInstance;

}

//    private View myView = null;
//    public void setMyView(View myView) {
//        this.myView = myView;
//    }

//用弱引用
private WeakReference<View> myView = null;
public void setMyView(View myView) {
    this.myView = new WeakReference<View>(myView);
}

很多東西雖然簡單爽醋,還是有我們需要注意的地方。這就需要我們理解它們的特性了土匀。比如上面用了弱引用來解決內(nèi)存泄漏的問題子房,那我們就需要明白弱引用的特點(diǎn),需要注意使用弱引用的變量可能為空的問題

被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾收集發(fā)生之前就轧,當(dāng)垃圾收集器工作時(shí),無論當(dāng)前內(nèi)存是否足夠田度,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對象


             今天你進(jìn)步了嘛妒御?歡迎關(guān)注我的微信公眾號,和我一起每天進(jìn)步一點(diǎn)點(diǎn)镇饺!
AntDream
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乎莉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子奸笤,更是在濱河造成了極大的恐慌惋啃,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件监右,死亡現(xiàn)場離奇詭異边灭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)健盒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進(jìn)店門绒瘦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扣癣,你說我怎么就攤上這事惰帽。” “怎么了父虑?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵该酗,是天一觀的道長。 經(jīng)常有香客問我士嚎,道長呜魄,這世上最難降的妖魔是什么烁焙? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮耕赘,結(jié)果婚禮上骄蝇,老公的妹妹穿的比我還像新娘。我一直安慰自己操骡,他們只是感情好九火,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著册招,像睡著了一般岔激。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上是掰,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天虑鼎,我揣著相機(jī)與錄音,去河邊找鬼键痛。 笑死炫彩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的絮短。 我是一名探鬼主播江兢,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼丁频!你這毒婦竟也來了杉允?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤席里,失蹤者是張志新(化名)和其女友劉穎叔磷,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奖磁,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡改基,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了署穗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寥裂。...
    茶點(diǎn)故事閱讀 39,773評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖案疲,靈堂內(nèi)的尸體忽然破棺而出封恰,到底是詐尸還是另有隱情,我是刑警寧澤褐啡,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布诺舔,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏低飒。R本人自食惡果不足惜许昨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望褥赊。 院中可真熱鬧糕档,春花似錦、人聲如沸拌喉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尿背。三九已至端仰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間田藐,已是汗流浹背荔烧。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汽久,地道東北人鹤竭。 一個(gè)月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像回窘,于是被迫代替她去往敵國和親诺擅。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 所有知識點(diǎn)已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)啡直? 在 Jav...
    侯蛋蛋_閱讀 2,430評論 1 4
  • 在一個(gè)方法內(nèi)部定義的變量都存儲(chǔ)在棧中,當(dāng)這個(gè)函數(shù)運(yùn)行結(jié)束后苍碟,其對應(yīng)的棧就會(huì)被回收酒觅,此時(shí),在其方法體中定義的變量將不...
    Y了個(gè)J閱讀 4,417評論 1 14
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,374評論 8 265
  • 本文原創(chuàng)微峰,轉(zhuǎn)載請注明來處 上一篇文章寫了設(shè)計(jì)師與產(chǎn)品經(jīng)理如何進(jìn)行用戶需求分析舷丹,而在需求采集的過程中免不了經(jīng)歷和用戶...
    阿希axi閱讀 459評論 0 1
  • 寒沁簾櫳卷,秋聲徹故園蜓肆。 臨風(fēng)聽雨咽颜凯,倚戶望潮痕。 濁浪千帆競仗扬,青云萬乘奔症概。 乾坤何以報(bào),遐志在心門早芭。 (平水韻·...
    不惑而歌閱讀 493評論 13 40