單例模式 (懶漢式, 執行緒同步詳解)
單例模式(懶漢式)
在懶漢式寫法中, 我們需要非常注意執行緒同步的問題. 大概有一下幾個:
1. getInstance() 直接鎖方法好不好
2. 雙重鎖定
3. synchronized(this)行不行
1. getInstance() 直接鎖方法好不好
這種寫法:
class Singleton {
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null ){
instance = new Singleton();
}
return instance;
}
}
我們可以看到這裡synchronized是鎖方法, 當兩個執行緒都要進入getInstance()時, 只有一個能進入, 並創建出例項, 然後另外一個進入後, 判斷 instace不為null, 然後直接得到instance. 這種做法是沒有錯誤的. 但是由於執行緒都需要通過getInstance()來獲取物件, 所以getInstance()呼叫頻率很高, 所以執行緒被鎖的頻率也很高, 所以這種做法效率低.
2. 雙重鎖定
由於上面效率的原因, 你可能就會想到把 syschronized 放在 getInstance()裡面, 這種可避免在呼叫getInstance()時的阻塞問題, 如下:
class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
instance = new Singleton();
}
}
return instance;
}
}
這種寫法看似沒有問題, 其實卻有一個很大的隱患, 在於: 如果兩個執行緒同時執行getInstance(),判斷 instance都不為null後, 進入if判斷語句. 這個時候一個執行緒獲得鎖, 然後進入new了一個物件, 並開心的執行完了. 這個時候另外一個執行緒獲得了鎖, 但讓它也不會再去判斷 instace是否為null, 所以它也會再執行一次new操作. 所以這裡執行了兩次new操作. 當然最後instance還是隻指向後一次new的物件.
所以這個時候需要雙重鎖定, 就是在 synchronized中再加一次 null判斷, 如下:
class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
這樣就可以保證不會new兩次, 也是相對比較正確的, 並且效率也很高.
3. synchronized(this)行不行
答案是不行的, 如果你寫程式碼看一看, 直接就提示語法錯誤了, 因為我們的 getInstance() 方法是 static的, 所以裡面不能使用 this.
好了以上三點終結, 希望對大家有幫助.