單例模式的定義
單例模式保證一個類在全局中只有一個實例對象。
單例模式的幾種實現方式
餓漢模式
在類加載的時候就已經實例化對象,沒有延遲加載,但由于類加載的機制能保證線程安全,典型的空間換時間;
餓漢模式
懶漢模式
只有在調用getInstance()的時候才會實例化對象,但需要加上synchronized關鍵字保證線程安全,具有延遲加載的功能,
典型的時間換空間,與餓漢相比會比較消耗性能。
懶漢模式
DCL雙重檢查模式
懶漢模式的改進版,主要有兩個地方的改進:同步鎖改進和線程安全改進
同步改進:采用了雙重檢查的模式,第一層檢查避免了不必要的加鎖同步;第二層檢查保證實例為null時才會初始化實例對象;
線程安全改進:實例變量采用了volatile關鍵字聲明,volatile的語義能禁止指令重排序;在JVM中有一種優化機制叫亂序執行,也就是說為了執行效率,CPU可能不會按照代碼的順序執行,但是最終的執行結果跟代碼順序執行的結果是一致的。在這里volatile主要是解決DCL模式失效的問題,因為在new Singleton()初始化對象時不是一個原子操作,主要有三步:
(1)為實例變量分配內存;
(2)調用構造函數;
(3)實例變量執行已經分配的內存地址;
如果CPU亂序執行的話,有可能會按(1)(3)(2)的順序執行,如果此時有線程A和線程B,線程A執行完了(3)還未執行(2)之前,切換到了線程B,那么此時線程B獲得的實例就是非null的,線程B就直接返回該未初始化完成的實例對象,導致DCL失效。
DCL雙重檢查模式
靜態內部類
其實就是餓漢模式的改進版本,增加了延遲加載的功能。不會在類加載的時候就實例化對象,只有在調用getInstance()的時候才會實例化,同時由于內部類加載的方式可以保證線程安全,寫起來也比較簡單。
靜態內部類
補充
1、單例的構造函數必須聲明為private,保證不會被外界通過new的方式進行實例化;