??越來越感覺編程的世界是真正符合自然的世界,真正遵循自然的規律。在程序的世界中事情幾乎是平等的,即使不平等也是有據可循,任何技術問題也可討論的,無論結果如何,總是0與1的差別。 同樣真實世界中的事物放到程序世界中也變得那么客觀起來,正如這篇文章的主角Singleton Design Pattern一樣。
??單例模式可以有多種實現方法,需要根據情況作出正確的選擇。
- 餓漢式
public class Singleton {
private static Singleton mSingleton = new Singleton();
public static Singleton getInstance() {
return mSingleton;
}
}
缺點:在類加載的時候就完成了初始化,所以類加載比較慢
- 懶漢式
public class Singleton {
private static Singleton mSingleton;
public static Singleton getInstance() {
if (mSingleton == null) {
mSingleton = new Singleton();
}
return mSingleton;
}
}
缺點:在多線程情況上不能正常工作
- 雙重檢查鎖定(double-checked locking)
public class Singleton {
private static Singleton mSingleton;
public static Singleton getInstance() {
if (mSingleton == null) {
synchronized (Singleton.class) {
if (mSingleton == null) {
mSingleton = new Singleton();
}
}
}
return mSingleton;
}
}
缺點:在多線程運行的時候,mSingleton可能報空指針的錯誤。假設線程A運行getInstance()方法,當運行 mSingleton = new Singleton()的時候,線程B也調用getInstance()方法,因為mSingleton !=null(但實際上初始化有問題),直接返回mSingleton ,導致空指針問題。WTF?
??這是因為 mSingleton = new Singleton()這一步操作不是原子性操作(所謂原子操作是指不會被線程調度機制打斷的操作;這種操作一旦開始,就一直運行到結束,中間不會有任何 context switch ),在執行的過程中,需要3步處理1.mSingleton 分配內存 2.mSingleton 調用起構造函數 3.對mSingleton 指向內存分配區域 1--2--3,但有點時候jvm會將其重排序,可能1--3--2,在線程B調用的時候,雖然mSingleton 不是null,但它可能沒有進行初始化,導致空指針。
- volatile關鍵字
?volatile能解決上面這種問題
private volatile static Singleton mSingleton;
mSingleton現在由volatile 關鍵字修飾,禁止重排序并且保證了變量的可見性。
- 靜態內部類
public class Singleton {
public static Singleton getInstance() {
return Holder.mSingleton;
}
private static class Holder {
private static Singleton mSingleton = new Singleton();
}
}
??內部類分為對象級別和類級別,類級內部類指的是:有static修飾的成員變量的內部類。如果沒有static修飾的成員變量的內部類成為對象級內部類。
??類級內部類相當于外部類static成員,不存在依賴關系,相互獨立,只有在第一次使用時才會被初始化。
??當調用getInstance() 方法時,內部類Holder類才得到初始化;Singleton實例屬于靜態的域,由虛擬機來保證它的線程安全。
??這個模式優勢在于,getInstance()方法并沒有被同步,只是執行一個域的訪問,因為延時初始化并沒有增加任何訪問成本。