單例模式相關
單例物件的類只能允許一個例項存在,意味著只有通過該類提供的靜態方法來得到該類的唯一例項,開發過程中我們通常需要一個全域性物件進行一些資料、配置等的管理,但在開發過程中經常會有多執行緒的互動問題,因此我們使用單例模式時,寫法可採用 餓漢式、靜態內部類、列舉方式實現,其中靜態內部類方式對我們Android開發來說更適合。
1. 餓漢式空間換時間
優點:寫法簡單,類裝載時就完成例項化,避免了執行緒同步問題
缺點:類裝載時就完成例項化意味著沒有達到延遲裝載的效果,意味著就算不使用這個類也會佔據著記憶體
class SingletonOne{ private static SingletonOne singletonOne = new SingletonOne(); private SingletonOne() { } public static SingletonOne getInstance() { return singletonOne; } }
2.靜態內部類延遲載入,執行緒安全
優點:對比餓漢式方式來說既實現了執行緒安全的單例,又能滿足延遲載入的效果,且效率高
SingletoHolder在SingleTwo被裝載時並不會立即例項化,而在getInstance時才會裝載,類的靜態屬性只會在第一次載入類的時候初始化,所以這裡是靠JVM的裝載機制保證了執行緒的安全性
class SingletonTwo{ private SingletonTwo() { } private static class SingletonHolder{ private static SingletonTwo singletonTwo = new SingletonTwo(); } public static SingletonTwo getInstance() { return SingletonHolder.singletonTwo; } }
3.列舉方式
優點:因為預設列舉例項的建立是執行緒安全的並在任何情況下都是一個單例
缺點:在Android中列舉佔據的記憶體會比靜態常量超過兩倍多,所以儘量避免使用列舉
enum SingletonThree{ INSTANCE; public void doSomething(){}; }
4. 不推薦寫法時間換空間
此處基本是懶漢式方法的變種,演變趨至複雜,不建議這樣寫
懶漢式-1
優點:實現了延遲載入
缺點:只能在單執行緒下使用
class SingletonF_1 { private static SingletonF_1 singletonF_1; private SingletonF_1() { } public static SingletonF_1 getInstance() { singletonF_1 = new SingletonF_1(); return singletonF_1; } }
懶漢式-2
優點:實現了執行緒安全,延遲載入
缺點:每次獲取例項都要進入同步方法,效率低下
class SingletonF_2 { private static SingletonF_2 singletonF_2; private SingletonF_2() { } public static synchronized SingletonF_2 getInstance() { singletonF_2 = new SingletonF_2(); return singletonF_2; } }
懶漢式-3
優點:延遲載入,解決上方的每次都進入同步問題
缺點:帶來了執行緒不安全,當兩個執行緒都走到判空處時,兩個執行緒先後執行同步塊方法將生成兩個物件
class SingletonF_3 { private static SingletonF_3 singletonF_3; private SingletonF_3() { } public static SingletonF_3 getInstance() { if (singletonF_3 == null) { synchronized (SingletonF_3.class){ singletonF_3 = new SingletonF_3(); } } return singletonF_3; } }
懶漢式-4 又稱雙重校驗
優點:延遲載入,解決上方的執行緒不安全問題
缺點:這種寫法是完美理論但在執行過程中,因為指令重排序的問題,可能導致引用的物件雖不為空但還未完成例項化導致出錯
指令重排序:JVM對語句執行的優化,只要語句間沒有依賴,那JVM就可能會對它們進行重排序
如singletonF_4 = new SingletonF_4();
這段程式碼不具有原子性,在執行過程中會被拆分為好幾步
-
- 分配空間給物件
-
- 在空間內建立物件
-
- 將物件賦值給引用singlteoF_4
2依賴於1,但2與3不存在依賴,因此他的執行順序既可能是1>2>3,也可能是1>3>2,如果是1>3>2,當執行緒A執行到3時,執行緒B執行到第一次判空,此時的singlteoF_4已經不為null但是還未完全初始化,此時會發生異常
class SingletonF_4 { private static SingletonF_4 singletonF_4; private SingletonF_4() { } public static SingletonF_4 getInstance() { if (singletonF_4 != null) { synchronized (SingletonF_4.class){ if (singletonF_4 == null) { singletonF_4 = new SingletonF_4(); } } } return singletonF_4; } }
懶漢式-5雙重校驗優化--volatile 關鍵字
優點:解決指令重排序
缺點:需要在原生代碼中插入許多的記憶體屏障指令保證不發生亂序執行,這樣導致程式執行變慢
volatile主要作用
- 可見性 volatile 修飾的變數一旦發生更改會立即更新到主存當中去,未被修飾的變數則不可預估
- 有序性 禁止指令重排序可以保證初始化的有序性
class SingletonF_5 { private volatile static SingletonF_5 singletonF_5; private SingletonF_5() { } public static SingletonF_5 getInstance() { if (singletonF_5 != null) { synchronized (SingletonF_5.class){ if (singletonF_5 == null) { singletonF_5 = new SingletonF_5(); } } } return singletonF_5; } }
懶漢式-6雙重校驗優化--變相保證有序執行
優點:解決指令重排序的
class SingletonF_6 { private static SingletonF_6 singletonF_6; private SingletonF_6() { } public static SingletonF_6 getInstance() { if (singletonF_6 != null) { synchronized (SingletonF_6.class) { SingletonF_6 temp = null; try { temp = new SingletonF_6(); } catch (Exception e) { } if (temp != null){ //為什麼要做這個看似無用的操作,因為這一步是為了讓虛擬機器執行到這一步的時會才對singleton賦值, // 虛擬機器執行到這裡的時候,必然已經完成類例項的初始化。 // 所以這種寫法的DCL(Double Check LockDCL雙重檢查鎖)是安全的。 // 由於try的存在,虛擬機器無法優化temp是否為null singletonF_6 = temp; } } } return singletonF_6; } }
參考連結:
指令重排序https://blog.csdn.net/a_842297171/article/details/79316591
懶漢式優化https://www.jianshu.com/p/c80ac10db854
volatile關鍵字https://blog.csdn.net/nugongahou110/article/details/49927667