1.餓漢式html
public class Singleton { private static Singleton instance = new Singleton(); private Singleton(){ … } public static Singleton getInstance(){ return instance; } }
缺點:第一次加載類的時候會連帶着建立Singleton實例,這樣的結果與咱們所指望的不一樣,由於建立實例的時候可能並非咱們須要這個實例的時候。同時若是這個Singleton實例的建立很是消耗系統資源,而應用始終都沒有使用Singleton實例,那麼建立Singleton消耗的系統資源就被白白浪費了。安全
2.懶漢式多線程
public class Singleton{ private static Singleton instance = null; private Singleton(){ … } public static Singleton getInstance(){ if (instance == null) instance = new Singleton(); return instance; } }
缺點:存在多線程併發問題,併發下可能建立多個對象。併發
3.解決併發問題函數
方法上使用Class鎖機制線程
public static synchronized Singleton getInstance(){ if (instance == null) instance = new Singleton(); return instance; }
缺點:加載方法上鎖粒度太大,影響併發。實際上當單例實例被建立之後,其後的請求沒有必要再使用互斥機制了。htm
用double-checked locking將鎖縮減至代碼塊級別對象
public static Singleton getInstance(){ if (instance == null) synchronized(instance){ if(instance == null) instance = new Singleton(); } return instance; }
對於JVM而言,它執行的是一個個Java指令。在Java指令中建立對象和賦值操做是分開進行的,也就是說instance = new Singleton();語句是分兩步執行的。可是JVM並不保證這兩個操做的前後順序,也就是說有可能JVM會爲新的Singleton實例分配空間,而後直接賦值給instance成員,而後再去初始化這個Singleton實例。這樣就使出錯成爲了可能。blog
使用volatile防止重排序:排序
public class Singleton{ private static volatile Singleton instance = null; private Singleton(){ … } public static Singleton getInstance(){ if (instance == null) synchronized(instance){ if(instance == null) instance = new Singleton(); } return instance; }
不加鎖:爲了實現慢加載,而且不但願每次調用getInstance時都必須互斥執行。
public class Singleton{ private Singleton(){ … } private static class SingletonContainer{ private static Singleton instance = new Singleton(); } public static Singleton getInstance(){ return SingletonContainer.instance; } }
JVM內部的機制可以保證當一個類被加載的時候,這個類的加載過程是線程互斥的。這樣當咱們第一次調用getInstance的時候,JVM可以幫咱們保證instance只被建立一次,而且會保證把賦值給instance的內存初始化完畢,這樣咱們就不用擔憂3.2中的問題。此外該方法也只會在第一次調用的時候使用互斥機制,這樣就解決了3.1中的低效問題。最後instance是在第一次加載SingletonContainer類時被建立的,而SingletonContainer類則在調用getInstance方法的時候纔會被加載,所以也實現了惰性加載。
https://www.cnblogs.com/jingpeipei/p/5771716.html
/** 推薦
* 靜態內部類實現單例模式(懶加載的一種)
* 靜態內部類只有在調用的時候才初始化,而且初始化過程線程安全。
* instance不提供修改實例方法,靜態變量只初始化一次,因此final修飾不加也同樣。
* 兼備了併發高效調用和懶加載的優點。
* 結論:線程安全,調用效率高,而且能夠延時加載。
*/
public class StaticClassSingleton {
//私有構造函數
private StaticClassSingleton(){};
//靜態內部類
private static class SingletonClassInstance{
private static final StaticClassSingleton INSTANCE =
new StaticClassSingleton();
}
//提供實例的調用方法
public static StaticClassSingleton getInstance(){
return SingletonClassInstance.INSTANCE;
}
}
/** 推薦
* 枚舉類對象自己就是一個單例對象。
* 沒有延時加載的特性。
* 能夠自然的防止反射和反序列化的漏洞,由於枚舉是基於JVM底層實現的。
* 結論:線程安全,調用效率高,可是不是延時加載,自然防止反射和反序列化漏洞。
*/
public enum EnumSingleton {
/**
* 枚舉對象
*/
INSTANCE;
/** * 成員方法 */ public void singletonOperation(){ //單例對象的其它操做實現。 } }