在實現單例模式時,如果未考慮多執行緒的情況,就容易寫出下面的錯誤**:
public
class
singleton
public singleton getinstance()
return uniquesingleton;
}}
在多執行緒的情況下,這樣寫可能會導致uniquesingleton
有多個例項。比如下面這種情況,考慮有兩個執行緒同時呼叫getinstance()
:
time
thread a
thread b
t1檢查到uniquesingleton
為空
t2檢查到uniquesingleton
為空
t3初始化物件a
t4返回物件a
t5初始化物件b
t6返回物件b
可以看到,uniquesingleton
被例項化了兩次並且被不同物件持有。完全違背了單例的初衷。
出現這種情況,第一反應就是加鎖,如下:
public class singleton
public synchronized singleton getinstance()
return uniquesingleton;
}}
這樣雖然解決了問題,但是因為用到了synchronized
,會導致很大的效能開銷,並且加鎖其實只需要在第一次初始化的時候用到,之後的呼叫都沒必要再進行加鎖。
雙重檢查鎖(double checked locking)是對上述問題的一種優化。先判斷物件是否已經被初始化,再決定要不要加鎖。
public
class
singleton
public singleton getinstance()
}}return uniquesingleton;
}}
如果這樣寫,執行順序就成了:
檢查變數是否被初始化(不去獲得鎖),如果已被初始化則立即返回。
獲取鎖。
再次檢查變數是否已經被初始化,如果還沒被初始化就初始化乙個物件。
執行雙重檢查是因為,如果多個執行緒同時了通過了第一次檢查,並且其中乙個執行緒首先通過了第二次檢查並例項化了物件,那麼剩餘通過了第一次檢查的執行緒就不會再去例項化物件。
這樣,除了初始化的時候會出現加鎖的情況,後續的所有呼叫都會避免加鎖而直接返回,解決了效能消耗的問題。
上述寫法看似解決了問題,但是有個很大的隱患。例項化物件的那行**(標記為error的那行),實際上可以分解成以下三個步驟:
分配記憶體空間
初始化物件
將物件指向剛分配的記憶體空間
但是有些編譯器為了效能的原因,可能會將第二步和第三步進行重排序,順序就成了:
分配記憶體空間
將物件指向剛分配的記憶體空間
初始化物件
現在考慮重排序後,兩個執行緒發生了以下呼叫:
time
thread a
thread b
t1檢查到uniquesingleton
為空
t2獲取鎖
t3再次檢查到uniquesingleton
為空
t4為uniquesingleton
分配記憶體空間
t5將uniquesingleton
指向記憶體空間
t6檢查到uniquesingleton`不為空
t7訪問uniquesingleton
(此時物件還未完成初始化)
t8初始化uniquesingleton
在這種情況下,t7時刻執行緒b對uniquesingleton
的訪問,訪問的是乙個初始未完成的物件。
public
class
singleton
public singleton getinstance()
}}return uniquesingleton;
}}
為了解決上述問題,需要在uniquesingleton
前加入關鍵字volatile
。使用了volatile關鍵字後,重排序被禁止,所有的寫(write)操作都將發生在讀(read)操作之前。
至此,雙重檢查鎖就可以完美工作了。
單例模式的雙重檢查
單例模式 public class singleton public static singleton getinstance return uniqueinstance 其中有兩次判斷是否為空的語句,第一次是為了提高效率,避免每次都要執行同步 塊,第二次判空,是為了避免多執行緒帶來的不安全,當兩個...
單例模式 雙重檢查鎖
單例模式分為餓漢式和懶漢式。餓漢式是事先分配記憶體,提前建立。這樣的方式為到位占用資源,當這種比較多時,會占用很多記憶體。懶漢式是在被呼叫的時候進行。這種在併發時又會導致問題。比較穩妥的辦法是在懶漢式的基礎上加上鎖,然後進行雙重檢查,這種springioc容器式單例也是用這種雙重檢查來避免執行緒衝突...
volatile引出的單例模式之雙重檢查
保證執行緒可見性 mesi,利用cpu的快取一致性協議 禁止指令重排序 cpu 在實現單例模式時,如果未考慮多執行緒的情況,就容易寫出下面的錯誤 public class singleton public singleton getinstance return uniquesingleton 在多...