目錄靜態內部類寫法
列舉寫法
防序列化攻擊處理
防反射攻擊處理
參考《建立單例模式的x種方法》在網上已經爛大街了,但這麼多方式,會加重我的記憶負擔,所以還得做個比較,把知識點濃縮一下,最終列出了三個比較常見的方法(其實是兩個,只有靜態內部類和列舉沒有隱患,雙檢鎖是有隱患的,請看下文中的防反射攻擊處理一節)
如果想要懶載入:
我更推薦靜態內部類,因為細節少,寫法簡單,不容易錯。
如果不想要懶載入:
推薦列舉,自帶防反射攻擊和防序列化攻擊,寫法簡單。
public class double_check_lock_test
public static double_check_lock_test getinstance()}}
return instance;
}}
兩個關鍵點,一是volatile防止指令重排,二是synchronized給類上鎖
因為 instance = new point(200,1); 這句話不是原子指令,其實有三步:
1.分配物件記憶體;
2.呼叫構造器,執行初始化;
3.將物件引用賦值給變數。
其中2和3可能發生指令重排,在併發環境下,執行緒a可能會先執行1和3,這時恰好切換到執行緒b,這時執行緒b就會看到乙個不為null,但是沒有完成初始化的物件,此時執行緒b訪問這個物件就會發生異常。(詳細請看雙重檢查鎖單例模式為什麼要用volatile關鍵字?)
public class static_test
static_test(){}
public static final static_test getinstance()
}
enum enum_test
}
在單例類中加上readresolve()方法。
private object readresolve()
在建構函式中加入檢查。如果發現已經建立過,則不再建立。以雙檢鎖為例:
private double_check_lock_test()
}
但是在《j**a單例---反射攻擊單例和解決方法》這篇文章中指出:
如果先通過正常的獲取手段獲取例項,再進行反射攻擊獲取例項,此時是能防得住反射攻擊的。
但如果反過來,先進行反射攻擊獲取例項,再通過正常的獲取手段獲取例項,得到的兩個結果不同。也就是說,對於雙檢鎖來說,這種處理依舊不能防止反射攻擊,網上的部分部落格都是錯的。
還有一種嘗試解決這種反射攻擊的是:在單例裡面加標識屬性,如果例項化之後,標識改變,在構造器裡面判斷標識改變就拋異常,和上面這種氣勢差不多,但是沒用的,反射可以把構造器的許可權放開,同樣可以把屬性的許可權放開,並且修改屬性值,所以這種方式也是不行的。
但是這種處理在靜態內部類中,卻不會產生上面雙檢鎖出現的"先進行反射攻擊獲取例項,再通過正常的獲取手段獲取例項,得到的兩個結果不同"的情況。
static_test()
}
具體原因我還沒弄清楚,希望看到的朋友可以解答一下。
雙重檢查鎖單例模式為什麼要用volatile關鍵字?
j**a單例---反射攻擊單例和解決方法
單例模式的常用寫法
單例模式是最常用到的設計模式之一,熟悉設計模式的朋友對單例模式都不會陌生。一般介紹單例模式的書籍都會提到 餓漢式 和 懶漢式 這兩種實現方式。但是除了這兩種方式,本文還會介紹其他幾種實現單例的方式,讓我們來一起看看吧。單例模式有很多種實現方式,下面給出我經常使用的一種方式 單例模式是一種常用的軟體設...
單例模式寫法
單例模式是最常用到的設計模式之一,熟悉設計模式的朋友對單例模式都不會陌生。一般介紹單例模式的書籍都會提到餓漢式和懶漢式這兩種實現方式。但是除了這兩種方式,本文還會介紹其他幾種實現單例的方式,讓我們來一起看看吧。單例模式是一種常用的軟體設計模式,其定義是單例物件的類只能允許乙個例項存在。許多時候整個系...
單例模式幾種寫法
1.餓漢式 public class singleton public static singleton getinstance 2.懶漢式 public class singleton 雙層檢測鎖 public static singleton getinstance return instanc...