Android中的單例模式
阿新 • • 發佈:2019-02-12
單例模式在設計模式中是最方便人們理解的。雖然容易理解,但裡面坑挺多的,所以在面試題中經常哪來做考題。簡單來說,單例模式,就是為了保證類物件只有並且只能建立一個。規模比較大的類,或者需要反覆使用的類,才會使用單例模式。下面一起探討一下單例模式的幾種方式。
懶漢式,執行緒不安全
當提到單例模式的時候,可能很多人會在第一時間想到如下的程式碼。
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance |
懶漢式,執行緒安全
為了解決上述的問題,其實很簡單,只需要在getInstance()加一個同步鎖(synchronized)就可以了.public如果多個執行緒進入,只允許進入一個,且執行完之後,才能允許另一個執行緒進入。static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
雙重檢驗鎖
雙重檢驗鎖,是一種使用同步塊加鎖的方法。因為會有兩次if判斷檢查instance是否為空,一次是在同步塊外,一次是在同步塊內。public static這段程式碼看起來很完美,不過有問題,主要是instance = newSingleton()這句,這並非一個原子操作,事實上在JVM中這句話大概做了下面三件事情。 1.給instance分配記憶體 2.呼叫Singleton的建構函式來初始化成員變數 3.將instance物件指向分配的記憶體空間(執行完這一步instance就非null了) 但是在JVM的即時編譯器中存在指令重排序的優化。也就是說上面的第二步和第三部的順序是不能保證的,最終的執行順序可能是1-2-3,也可能是1-3-2.如果是後者,則在3執行完畢、2未執行之前,被執行緒二搶佔了。這時instance已經是非null了(但卻沒有初始化),所以執行緒二會直接返回instance,然後使用,然後就GG報錯了。 我們只需要把instance變數宣告成volatile就可以了。Singleton getSingleton() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance ; }
public class Singleton { private volatile static Singleton instance; //宣告成 volatile private Singleton (){} public static Singleton getSingleton() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }在多執行緒併發中synchronized和Volatile都扮演著重要的角色,Volatile是輕量級的synchronized,它在多處理器開發中保證了共享變數的“可見 性”。可見性的意思是當一個執行緒修改一個共享變數時,另外一個執行緒能讀到這個修改的值。它在某些情況下比synchronized的開銷更小。
餓漢式 static final field
這種方法很簡單,因為單利的例項被宣告稱static和final變量了,在第一次載入類到記憶體中時就會初始化,所以建立例項本身是執行緒安全的。public class Singleton{ //類載入時就初始化 private static final Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }這種寫法缺點是它不是一種懶漢式載入模式,單例會在載入類後一開始就被初始化,即時客戶端沒有呼叫getInstance()方法,餓漢式的建立方式在 一些場景中無法使用:譬如Singleton例項的建立時依賴引數或者配置檔案的,在getInstance()之前必須呼叫某個方法設定引數給它,那樣這種單例寫 法就無法使用了。
靜態內部類
個人傾向於靜態內部類的方法。
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } } |
列舉
這種方法很簡單,也是它最大的優點了。public enum EasySingleton{ INSTANCE; }通過EasySingleton.INSTANCE來訪問例項,比呼叫getInsance()方法方便多了。建立列舉預設執行緒是安全的,所以不必擔心double checked locki -ng而且還能防止反序列化導致重新建立新的物件。這種方法很少看到有人寫,大概是因為不熟悉。