DCL的單例一定是線程安全的嗎


讀了本文,你會知道,為什么不加volatile關鍵字的單例模式不是線程安全的

 

有經驗的開發者都知道雙重鎖定檢查(DCL,Double Check Lock)的單例是最優秀的,如下文所示:

 1 public class Singleton {
 2     private static Singleton instance = null;
 3     public  static Singleton getInstance() {
 4         if(null == instance) {    // 第一次檢查
 5             synchronized (Singleton.class) {
 6                 if(null == instance) {   // 加鎖后第二次檢查                 
 7                     instance = new Singleton();    
 8                 }
 9             }
10         }
11        
12         return instance; 
13         
14     }
15 }

 

這看上去一切都很完美,無懈可擊,但實際上這個 getInstance() 方法並不完美。問題出在哪里呢?出在 new 操作上,我們以為的 new 操作應該是:

  1. 分配一塊內存 M;
  2. 在內存 M 上初始化 Singleton 對象;
  3. 然后 M 的地址賦值給 instance 變量。

但是實際上,經過編譯器優化后的執行順序是這樣的:

  1. 分配一塊內存 M;
  2. 將 M 的地址賦值給 instance 變量;
  3. 最后在內存 M 上初始化 Singleton 對象。

如下圖所示,線程A進入<第一次檢查>,A先獲得Synchronize鎖,分配一塊內存M,先將M的地址賦值給了 instance 變量, 此刻發生線程切換,線程B檢測到instance不為空,直接返回未初始化的instance, 如果我們這個時候訪問 instance 的成員變量就可能觸發空指針異常。    

 

 

 

所以,我們會加對象上加 Volatile關鍵字,禁止指令重排序,

Volatile的原理,可以參考我的另一篇文章:Java多線程的volatile底層實現原理

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM