懶漢式寫法,包含演進過程,通過雙重檢查和靜態方法實現的方式保證線程安全;
//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;
}
}