參考地址:
應用最廣的模式-單列模式(結合Android源碼) -----微信http://mp.weixin.qq.com/s/ZfJPhEPp-NaY5ZFsBZzyYw
android設計模式源碼分析
https://github.com/simple-android-framework/android_design_patterns_analysis
Android中常用的2種模式
- 惡漢式
public class Singleton{
//嚴謹考慮 final 需要寫(可以不寫)
private static final Singleton singleton = new Singleton();
public static Singleton getInstance(){
return singleton;
}
private Singleton(){
}
}
- 懶漢式(延遲加載) 升級 雙重檢查單例( DCL 實現單例)
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(){
}
}
還會報錯的情況
創建一個變量需要哪些步驟呢?一個是申請一塊內存,調用構造方法進行初始化操作,另一個是分配一個指針指向這塊內存。這兩個操作誰在前誰在后呢?JVM規范并沒有規定。那么就存在這么一種情況,JVM是先開辟出一塊內存,然后把指針指向這塊內存,最后調用構造方法進行初始化。
下面我們來考慮這么一種情況:線程A開始創建SingletonClass的實例,此時線程B調用了getInstance()方法,首先判斷instance是否為null。按照我們上面所說的內存模型,A已經把instance指向了那塊內存,只是還沒有調用構造方法,因此B檢測到instance不為null,于是直接把instance返回了——問題出現了,盡管instance不為null,但它并沒有構造完成,就像一套房子已經給了你鑰匙,但你并不能住進去,因為里面還沒有收拾。此時,如果B在A將instance構造完成之前就是用了這個實例,程序就會出現錯誤了!
解決方案
在JDK 5之后,Java使用了新的內存模型。
當然這個bug已經修復了,SUN官方調整了JVM,具體了Volatile關鍵字,因此在jdk1.5之前只需要寫成這樣既可, private Volatitle static Singleton instance; 這樣就可以保證每次都是從主內存中取,當然這樣寫或多或少的回影響性能,但是為了安全起見,這點性能犧牲還是值得。
public class SingletonClass {
//volatile 避免出現單例失敗的情況 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() {
}
}
靜態內部類的方式
然而,這只是JDK1.5之后的Java的解決方案,那之前版本呢?其實,還有另外的一種解決方案,并不會受到Java版本的影響:
public class SingletonClass {
private static class SingletonClassInstance {
private static final SingletonClass instance = new SingletonClass();
}
public static SingletonClass getInstance() {
return SingletonClassInstance.instance;
}
private SingletonClass() {
}
}
在這一版本的單例模式實現代碼中,我們使用了Java的靜態內部類。這一技術是被JVM明確說明了的,因此不存在任何二義性。在這段代碼中,因為SingletonClass沒有static的屬性,因此并不會被初始化。直到調用getInstance()的時候,會首先加載SingletonClassInstance類,這個類有一個static的SingletonClass實例,因此需要調用SingletonClass的構造方法,然后getInstance()將把這個內部類的instance返回給使用者。由于這個instance是static的,因此并不會構造多次。
由于SingletonClassInstance是私有靜態內部類,所以不會被其他類知道,同樣,static語義也要求不會有多個實例存在。并且,JSL規范定義,類的構造必須是原子性的,非并發的,因此不需要加同步塊。同樣,由于這個構造是并發的,所以getInstance()也并不需要加同步。
至此,我們完整的了解了單例模式在Java語言中的時候,提出了兩種解決方案。個人偏向于第二種,并且Effiective Java也推薦的這種方式。