強烈建議用枚舉實現單例模式

懶漢式寫法,包含演進過程,通過雙重檢查和靜態方法實現的方式保證線程安全;

//1.懶漢式(延遲加載)
//線程不安全寫法
public class Methods{
	private Methods{};
	private static Methods instance = null;
	//獲取單例
	public static Methods getInstance(){
		if(instance==null){
			return new Methods();
		}
		return instance;
	}	
}
//雙重檢查方式寫法
public class Methods{
	private Methods{};
	private static Methods instance = null;
	//獲取單例
	public static Methods getInstance(){
		if(instance==null){
			//如果爲null也只有一個線程能進入,不滿足條件最終走最外部的那個return
			synchronized(Methods.class){
				if(instance==null){
					return new Methods();
				}
			}
		}
		return instance;
	}	

}
//上述的寫入在synchronized裏面的代碼 存在指令重排序的問題,如果先給instance分配內存地址,然後再去實例化對象,這樣先進入到線程已經複製,後進入的線程也無法獲取到
//雙重加鎖 防止指令重排序的寫法
public class Methods{
	private Methods{};
	private volatile static  Methods instance = null;
	//獲取單例
	public static Methods getInstance(){
		if(instance==null){
			//如果爲null也只有一個線程能進入,不滿足條件最終走最外部的那個return
			synchronized(Methods.class){
				if(instance==null){
					return new Methods();
				}
			}
		}
		return instance;
	}	

}
//基於靜態內部類的實現 原理是類初始化的時候,只有一個類能獲取到類初始化鎖
public class Methods{
	private Methods{};
	private static class InnerMethods{
		private static Methods methods = new Methods{};
	}
	public static Methods getInstance(){
		return InnerMethods.methods;
	}
	
}

餓漢式寫法很簡單,餓漢式最大的缺點就是沒有延遲加載的效果,如果一個單例從項目啓動一直都沒有被使用,會造成很大內存浪費;

//2.餓漢式(沒有延遲加載的效果)
public class Methods{
	private final static Methods methods = new Methods();
	private Methods{};
	public static Methods getInstance(){
		return methods;
	}
	
}

既然餓漢式和懶漢式的寫法看似已經非常完美了,那爲何還需要強調使用枚舉來實現單例模式;原因在於上述兩種實現方式有兩個很大的問題,就是序列化破壞和反射攻擊;

序列化攻擊:利用單例獲取的對象進行序列化,然後在將序列化的對象反序列化,對比兩者不相同;

反射攻擊:同樣的道理,通過反射獲取到的對象和實例化的對象,兩者不相等;

小夥伴可以親自試一試便知道,至於具體是什麼原因導致的,由於篇幅原因,這裏不贅述,小夥伴可以參考網上相關的資料;

那麼我們如何才能解決上述兩個問題呢,那就是我們這篇博客的主題,通過枚舉的方式實現單例模式;代碼也非常簡潔;

public enum Methods{
	INSTANCE;
	
	public static Methods getInstance(){
	  return INSTANCE; 
	}
	
}

 

發佈了59 篇原創文章 · 獲贊 3 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章