【設計模式】單例模式的絕對單例及執行緒安全
1. 餓漢模式
/**
* 餓漢模式
* @author 鄭海鵬
* @since 2015年7月6日
*/
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
public static void func() {
System.out.println("this is a function!" );
}
}
優點:
執行緒安全、絕對單例
缺點:
- 在多例項或者有其他靜態方法時,instance在沒有使用它的時候就已經載入好了。
例如呼叫Singleton.func(),這個方法中並沒有用到instance,但是instance也被初始化了。若該物件很大,則在沒使用時就載入很浪費記憶體。- 不能防止反序列化、反射產生新的例項。
2. 懶漢模式,延時載入
/**
* 懶漢模式
* @author 鄭海鵬
* @since 2015年7月6日
*/
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (null == instance) { // ①
instance = new Singleton();
}
return instance;
}
}
優點:
instance在沒有使用時不用載入,節省記憶體。
缺點:
- 執行緒不安全;
執行緒不安全原因:當多個執行緒執行到①時,都是發現instance為null,於是都去初始化了。- 不能防止反序列化、反射產生新的例項。
3. 方法鎖
/**
* 方法鎖
* @author 鄭海鵬
* @since 2015年7月6日
*/
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (null == instance) {
instance = new Singleton();
}
return instance;
}
}
優點:
執行緒安全,絕對單例
缺點:
- 加鎖導致程式併發性降低;
- 不能防止反序列化、反射產生新的例項。
4. 雙重檢查鎖 DCL
/**
* 雙重檢查鎖DCL
* @author 鄭海鵬
* @since 2015年7月6日
*/
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (null == instance) { // ①
synchronized (Singleton.class) {
if (null == instance) { // ②
instance = new Singleton(); // ③
}
}
}
return instance;
}
}
優點:
大部分時候執行緒安全,相比方法鎖程式併發性較高。
缺點:
- 執行緒並非絕對安全;
- 不能防止反序列化、反射產生新的例項。
執行緒不安全原因:
Java的亂序執行、初始化物件需要時間。
對於語句③,JVM在執行時大致做了下述三件事:
a. 在記憶體中分配一塊記憶體
b. 呼叫構造方法
c. 將記憶體的地址指向instance變數。(執行這一步後,instance != null)如果按照abc的順序執行也不會有什麼問題。但由於Java亂序執行的機制,有可能在真實情況下執行順序為acb。
假設t1、t2是兩個執行緒。t1執行到①時,發現為null,於是執行到語句③,先執行a,再執行c,在還沒有執行b時,時間片給了t2。這時,由於instance已經分配了地址空間,instance不為null了。所以t2在執行到語句①後直接return instance,獲得了這個還沒有被初始化的物件,然後在使用時就報錯了。
也就是說,在使用DCL時,有可能得到了例項的正確引用,但卻訪問到其成員變數的不正確值。
5. 雙重檢查鎖DCL與volatile的聯用
/**
* 雙重檢查鎖DCL與volatile的聯用
* @author 鄭海鵬
* @since 2015年7月6日
*/
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton(); // ③
}
}
}
return instance;
}
}
優點:
執行緒安全、絕對單例
缺點:
- 程式碼看起來有點亂;
- 不能防止反序列化、反射產生新的例項
執行緒安全的原因:我納悶這個很久,後來發現我只關注了volatitle的一個特性:對變數的讀取每次都是從主存中讀取。volatitle還有另外一個特性:volatile遮蔽指令重排序。也就是說,執行語句③時,肯定是按照abc的順序執行的(參見四)。
6. 靜態內部類 Initialization On Demand Holder
/**
* 靜態內部類 Initialization On Demand Holder
* @author 鄭海鵬
* @since 2015年7月6日
*/
public class Singleton {
private static class SingletonHolder{
static final Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
優點:
執行緒安全,絕對單例
缺點:
不能防止反序列化、反射產生新的例項
和餓漢模式的區別:餓漢模式會在沒有使用它的時候就已經載入例項。
執行緒安全的原因:Java的執行機制保證了static修飾的類、物件僅被初始化一次。
7. 列舉類
public enum Singleton {
instance {
String name = "this is name";
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
};
public abstract void setName(String name);
public abstract String getName();
}
優點:
執行緒安全、絕對單例、可以防止因為反序列化、反射產生新的例項
缺點:
和餓漢模式類似,會造成記憶體浪費。