參考地址:
應(yīng)用最廣的模式-單列模式(結(jié)合Android源碼) -----微信http://mp.weixin.qq.com/s/ZfJPhEPp-NaY5ZFsBZzyYw
android設(shè)計(jì)模式源碼分析
https://github.com/simple-android-framework/android_design_patterns_analysis
Android中常用的2種模式
- 惡漢式
public class Singleton{
//嚴(yán)謹(jǐn)考慮 final 需要寫(xiě)(可以不寫(xiě))
private static final Singleton singleton = new Singleton();
public static Singleton getInstance(){
return singleton;
}
private Singleton(){
}
}
- 懶漢式(延遲加載) 升級(jí) 雙重檢查單例( DCL 實(shí)現(xiàn)單例)
public class SingletonClass{
private static SingletonClass instance = null;
public static SingletonClass getInstance(){
//雙層判斷
if(instance == null){
synchronized(SingletonClass.class){
if(instance == null){
instance = new SingletonClass();
}
}
}
return instance;
}
private SingletonClass(){
}
}
還會(huì)報(bào)錯(cuò)的情況
創(chuàng)建一個(gè)變量需要哪些步驟呢悯周?一個(gè)是申請(qǐng)一塊內(nèi)存,調(diào)用構(gòu)造方法進(jìn)行初始化操作蹈矮,另一個(gè)是分配一個(gè)指針指向這塊內(nèi)存踱侣。這兩個(gè)操作誰(shuí)在前誰(shuí)在后呢媳友?JVM規(guī)范并沒(méi)有規(guī)定祷肯。那么就存在這么一種情況萨西,JVM是先開(kāi)辟出一塊內(nèi)存芙代,然后把指針指向這塊內(nèi)存,最后調(diào)用構(gòu)造方法進(jìn)行初始化茉继。
下面我們來(lái)考慮這么一種情況:線程A開(kāi)始創(chuàng)建SingletonClass的實(shí)例咧叭,此時(shí)線程B調(diào)用了getInstance()方法,首先判斷instance是否為null烁竭。按照我們上面所說(shuō)的內(nèi)存模型菲茬,A已經(jīng)把instance指向了那塊內(nèi)存,只是還沒(méi)有調(diào)用構(gòu)造方法派撕,因此B檢測(cè)到instance不為null婉弹,于是直接把instance返回了——問(wèn)題出現(xiàn)了,盡管instance不為null终吼,但它并沒(méi)有構(gòu)造完成镀赌,就像一套房子已經(jīng)給了你鑰匙,但你并不能住進(jìn)去际跪,因?yàn)槔锩孢€沒(méi)有收拾商佛。此時(shí),如果B在A將instance構(gòu)造完成之前就是用了這個(gè)實(shí)例姆打,程序就會(huì)出現(xiàn)錯(cuò)誤了良姆!
解決方案
在JDK 5之后,Java使用了新的內(nèi)存模型幔戏。
當(dāng)然這個(gè)bug已經(jīng)修復(fù)了,SUN官方調(diào)整了JVM,具體了Volatile關(guān)鍵字,因此在jdk1.5之前只需要寫(xiě)成這樣既可, private Volatitle static Singleton instance; 這樣就可以保證每次都是從主內(nèi)存中取,當(dāng)然這樣寫(xiě)或多或少的回影響性能,但是為了安全起見(jiàn),這點(diǎn)性能犧牲還是值得玛追。
public class SingletonClass {
//volatile 避免出現(xiàn)單例失敗的情況 jdk1.5以前
private volatile static SingletonClass instance = null;
public static SingletonClass getInstance() {
if (instance == null) {
synchronized (SingletonClass.class) {
if(instance == null) {
instance = new SingletonClass();
}
}
}
return instance;
}
private SingletonClass() {
}
}
靜態(tài)內(nèi)部類的方式
然而,這只是JDK1.5之后的Java的解決方案闲延,那之前版本呢痊剖?其實(shí),還有另外的一種解決方案垒玲,并不會(huì)受到Java版本的影響:
public class SingletonClass {
private static class SingletonClassInstance {
private static final SingletonClass instance = new SingletonClass();
}
public static SingletonClass getInstance() {
return SingletonClassInstance.instance;
}
private SingletonClass() {
}
}
在這一版本的單例模式實(shí)現(xiàn)代碼中邢笙,我們使用了Java的靜態(tài)內(nèi)部類。這一技術(shù)是被JVM明確說(shuō)明了的侍匙,因此不存在任何二義性。在這段代碼中叮雳,因?yàn)镾ingletonClass沒(méi)有static的屬性想暗,因此并不會(huì)被初始化。直到調(diào)用getInstance()的時(shí)候帘不,會(huì)首先加載SingletonClassInstance類说莫,這個(gè)類有一個(gè)static的SingletonClass實(shí)例,因此需要調(diào)用SingletonClass的構(gòu)造方法寞焙,然后getInstance()將把這個(gè)內(nèi)部類的instance返回給使用者储狭。由于這個(gè)instance是static的互婿,因此并不會(huì)構(gòu)造多次。
由于SingletonClassInstance是私有靜態(tài)內(nèi)部類辽狈,所以不會(huì)被其他類知道慈参,同樣,static語(yǔ)義也要求不會(huì)有多個(gè)實(shí)例存在刮萌。并且驮配,JSL規(guī)范定義,類的構(gòu)造必須是原子性的着茸,非并發(fā)的壮锻,因此不需要加同步塊。同樣涮阔,由于這個(gè)構(gòu)造是并發(fā)的猜绣,所以getInstance()也并不需要加同步。
至此敬特,我們完整的了解了單例模式在Java語(yǔ)言中的時(shí)候掰邢,提出了兩種解決方案。個(gè)人偏向于第二種擅羞,并且Effiective Java也推薦的這種方式尸变。