單例模式是設計模式中總容易理解,並且使用次數比較多的模式,往往在面試中會被問到。在這裡我簡述下自己的思路。希望能對你有所幫助。 ###懶漢式 單例模式
public class Single {
private static Single instance;
private Single () {}
public static Single getInstance () {
if (instance == null) {
instance = new Single();
}
return instance;
}
}
複製程式碼
上面這種,是我們眾所周知的,往往教科書上也是這麼寫的,但是這種是執行緒不安全的,那麼我們來看下一個。
懶漢式2
public static synchronized Single getInstance() {
if (instance == null) {
instance = new Single();
}
return instance;
}
複製程式碼
這種相對第一種來說,就安全了很多,但是它並不高效。
雙重檢驗鎖
雙重檢驗鎖模式(double checked locking pattern),是一種使用同步塊加鎖的方法。被稱其雙重檢驗鎖。是因為會有兩次檢驗instance == null
,一次是在同步塊外,一次是在同步塊內。
public static Single getInstance () {
if(instance == null) {
synchronized (Single.class) {
if(instance ==null) {
instance = new Single();
}
}
}
return instance;
}
複製程式碼
這樣是不是很完美了,可是還有問題,我們來看下 JVM 對instance = new Single()
這句話做了什麼
- 大概做了三件事: 1.給instance分配記憶體 2.呼叫Single的建構函式來初始化成員變數 3.將instance物件指向給分配的記憶體 JVM的即時編譯器中存在指令重排序的優化。這樣就會導致上邊的第二步和第三步的實行順序不確定。最終的執行順序可能是 1-2-3 也可能是 1-3-2。如果是後者,則在 3 執行完畢、2 未執行之前,被執行緒二搶佔了,這時 instance 已經是非 null 了(但卻沒有初始化),所以執行緒二會直接返回 instance,然後使用,然後順理成章地報錯。
public class Single() {
private volatile static Single instance; // 宣告成 volatile
private Single () {}
public static Single getInstance () {
if (instance == null) {
synchronized (Single.class) {
if(instance == null) {
instance = new Single();
}
}
}
return instance;
}
}
複製程式碼
餓漢式 static final field
public class Single{
//類載入時就初始化
private static final Single instance = new Single();
private Single(){}
public static Single getInstance(){
return instance;
}
}
複製程式碼
靜態內部類 static nested class
public class Single {
private static class SingleHolder {
private static final Single INSTANCE = new Single();
}
private Single (){}
public static final Single getInstance() {
return SingleHolder.INSTANCE;
}
}
複製程式碼
列舉 Enum
用列舉寫單例實在太簡單了!這也是它最大的優點。下面這段程式碼就是宣告列舉例項的通常做法。
public enum EasySingle{
INSTANCE;
}
複製程式碼