這次講講最簡單也最常用的單例模式(顧名思義 保證實例唯一的一種設計模式)
直接五種單例模式獻上,讓你瞭解單例模式的前世今生
餓漢模式
像一個餓漢一樣,不管需不需要,有沒有,都一定要去創建實例。因爲太餓了,不管三七二十一,我就要吃!!!
/*一、餓漢模式*/
private static Singleton singleton = new Singleton();
public static Singleton getSingleton() {
return singleton;
}
餓漢模式是在類初始化的時候就創建了實例,所以不管用不用都創建了實例,但是是線程安全的,因爲靜態變量的創建,在類的初始化過程中是保證線程安全的。
- 優點:線程安全,讀取變量速度快
- 缺點:因爲一開始就創建了變量,如果後面沒用到,就有可能浪費資源
懶漢模式 (不考慮線程安全)
像一個懶漢一樣,需要的時候纔去實例化,不需要我就不實例化。
/*二、懶漢模式-線程不安全模式*/
private static Singleton singleton2;
public static Singleton getSingleton2() {
if (singleton2 == null) {
singleton2 = new Singleton();
}
return singleton2;
}
- 優點:需要的時候纔會實例化變量,實現懶加載
- 缺點:線程不安全
懶漢模式 (線程安全)
這種較上面升級了一點,就是考慮到線程安全,當兩個線程同時操作怎麼辦,肯定要加鎖啦
/* 三、懶漢模式-線程安全模式
* 增加synchronized實現實例同步
* */
private static Singleton singleton3;
public synchronized static Singleton getSingleton3() {
if (singleton3 == null) {
singleton3 = new Singleton();
}
return singleton3;
}
synchronized修飾符保證同一時間只有一個線程能進入該方法
- 優點:線程安全,懶加載
- 缺點:需要每次都走鎖的部分,性能不算很好
雙重加鎖
這種就是我們代碼中常用的啦,雙重加鎖的同時,用volatile修飾變量
private volatile static Singleton singleton4;
public static Singleton getSingleton4() {
if (singleton4 == null) {
synchronized (Singleton.class) {
if (singleton4 == null) {
singleton4 = new Singleton();
}
}
}
return singleton4;
}
這種模式在保證線程安全的同時提高了性能:
-
synchronized加鎖使得同一時間只有一個線程能進入
-
外面又加了一層if判斷其實就是爲了性能,如果不爲空就不需要進入下面鎖的部分了,直接返回
-
volatile修飾符爲了讓singleton4實例在變化後立即寫入主存,方便其他線程讀取,否則有可能造成空指針,因爲new的過程不是一瞬間的,所以有可能在操作過程中,另一個線程讀到singleton4還是空的。
-
優點:線程安全,懶加載,性能也還可以
-
缺點:有點複雜,可能加載速度不快
靜態內部類模式-號稱最優雅單例
這種方法精髓就在於比較優雅,代碼量少,簡單易懂。
第一次調用方法時候,纔會去加載SingletonHolder內部類並且實例化INSTANCE
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getSingleton5() {
return SingletonHolder.INSTANCE;
}
ok,你說只會存在一個實例我是看到了,但是這個爲啥就能保證線程安全了呢?
這就要說到類的初始化了,類初始化階段是類加載過程的最後一步,也是執行類構造器()方法的過程。
而虛擬機會保證一個類的()方法在多線程環境中被正確地加鎖和同步。如果有多個線程去同時初始化一個類,那麼只會有一個線程去執行這個類的()方法,其它線程都需要阻塞等待,直到活動線程執行()方法完畢。
所以,明白了吧,內部類在初始化過程中是線程安全的,所以就能保證這個單例的創建也是線程安全的。
- 優點:線程安全,懶加載,代碼量少,簡單易懂
- 缺點:在調用getSingleton5方法不能帶上參數進行實例化,比如上下文參數Context
在Android中的應用
應該隨處可見吧,當某個實例在app中被多次調用,就需要創建一個單例,不讓其多次創建。
一般就選用雙重加鎖或者靜態內部類模式即可。