1.定義
- 一個類只有一個實例氓鄙,并且只有一個全局獲取入口嚼蚀。
2.適用場景
- 某個實例對象頻繁被訪問。
- 某個實例占用的資源較多昵仅。
3.實現(xiàn)方式
3.1 懶漢模式(線程不安全)
這樣可能會出現(xiàn)線程不同的方法睦霎,所以必須對getSingleton方法進行同步梢卸。
public class Singleton {
private static Singleton singleton;//私有靜態(tài)變量
private Singleton(){};//私有構(gòu)造方法
//全局靜態(tài)訪問入口
public static Singleton getSingleton(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
}
3.2 懶漢模式(線程安全)
public class Singleton {
private static Singleton singleton;//私有靜態(tài)變量
private Singleton(){};//私有構(gòu)造方法
//全局靜態(tài)訪問入口
public static synchronized Singleton getSingleton(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
}
這樣子線程就安全了,但是消耗了不必要的同步資源副女,不推薦這樣使用蛤高。
3.3 餓漢模式(線程安全)
public class Singleton {
private static Singleton singleton=new Singleton();//私有靜態(tài)變量
private Singleton(){};//私有構(gòu)造方法
//全局訪問入口
public Singleton getSingleton(){
return singleton;
}
}
避免線程安全問題,在聲明私有靜態(tài)變量時,就已經(jīng)實例化了類襟齿,不用考慮線程同步問題姻锁。
3.4 DCL模式(Double CheckLock)
public class Singleton {
private static Singleton singleton;//私有靜態(tài)內(nèi)部變量
private Singleton(){};//私有構(gòu)造方法
//全局訪問入口
public static synchronized Singleton getSingleton(){
if(singleton==null){
synchronized (Singleton.class){
if(singleton==null){
singleton=new Singleton();
}
}
}
return singleton;
}
}
通過兩個判斷,第一層是避免不必要的同步猜欺,第二層判斷是否為null位隶。
可能會出現(xiàn)DCL模式失效的情況。
DCL模式失效:
singleton=new Singleton();這句話執(zhí)行的時候开皿,會進行下列三個過程:
- 分配內(nèi)存涧黄。
- 初始化構(gòu)造函數(shù)和成員變量。
- 將對象指向分配的空間赋荆。
由于JMM(Java Memory Model)的規(guī)定笋妥,可能會出現(xiàn)1-2-3和1-3-2兩種情況。
所以窄潭,就會出現(xiàn)線程A進行到1-3時春宣,就被線程B取走,然后就出現(xiàn)了錯誤嫉你。這個時候DCL模式就失效了月帝。
Sun官方注意到了這種問題,于是就在JDK1.5之后幽污,具體化了volatile關(guān)鍵字嚷辅,這時候只用調(diào)整一行代碼即可。
private volatile static Singleton singleton;
3.5 靜態(tài)內(nèi)部類模式
public class Singleton {
private Singleton(){}
public static Singleton getSingleton(){
return SingletonHolder.singleton;
}
private static class SingletonHolder{
private final static Singleton singleton=new Singleton();
}
}
第一次加載Singleton類不會加載SingletonHolder類距误,但是調(diào)用getSingleton時簸搞,才會加載SingletonHolder,才會初始化singleton准潭。即確保了線程安全趁俊,又保證了單例對象的唯一性,延遲了單例的實例化刑然。這是最推薦的方式则酝。
3.6 枚舉模式
public enum Singleton{
singleton;
public void hello(){
System.out.print("hello");
}
}
這樣很簡單,線程時安全的闰集,并且避免了序列化和反射攻擊。
除了枚舉模式般卑,其他模式在實現(xiàn)了Serializable接口后武鲁,反序列化時單例會被破壞。所以要重寫readResolve()方法蝠检。
private Object readResolve() throws ObjectStreamException {
return INSTANCE;
}
3.7 使用容器實現(xiàn)單例模式
public class SingletonManger{
private static Map<String,Object> objectMap=new HashMap<String,Object>();
private SingletonManger(){}
public static void registerService(String key,Object singleton){
if(!objectMap.containsKey(key)){
objectMap.put(key,singleton);
}
}
public static Object getObjectService(String key){
return objectMap.get(key);
}
}
這樣可以將多個單例對象注入到HashMap中沐鼠,進行統(tǒng)一管理,更加方便快捷。