Java Double-Checked Locking 已死,C++ 呢?

來源:互聯網
上載者:User
已經有眾多文章討論 double-checked locking 模式在 Java 下面無法正常工作,這裡先簡要的總結一下。

根本原因在於 Java 的 memory model 允許所謂的 out-of-order write ,對於下面的 Java 代碼,out-of-order write 可能導致災難性的結果

public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance;
}

問題的起因在於語句 //3 ,JIT 所產生的彙編代碼所作的事情並不是先產生一個 Singleton 對象,然後將其地址賦予 instance 。相反,它的做法是

1. 先申請一塊空記憶體
2. 將其地址賦予 instance
3. 在 instance 所指的地址之上構建對象

下面的彙編代碼提供了證明,說明這不只是一個腦筋急轉彎,而是實際發生在 JIT 裡面的。代碼來自 Peter Haggar 的文章,我只是引用一下。

054D20B0   mov         eax,[049388C8]          ;load instance ref
054D20B5   test          eax,eax                         ;test for null
054D20B7   jne           054D20D7
054D20B9   mov         eax,14C0988h
054D20BE   call          503EF8F0                    ;allocate memory
054D20C3   mov         [049388C8],eax          ;store pointer in
                                                                              ;instance ref. instance 
                                                                              ;non-null and ctor
                                                                              ;has not run
054D20C8   mov         ecx,dword ptr [eax]
054D20CA   mov         dword ptr [ecx],1         ;inline ctor - inUse=true;
054D20D0   mov         dword ptr [ecx+4],5     ;inline ctor - val=5;
054D20D7   mov         ebx,dword ptr ds:[49388C8h]
054D20DD   jmp         054D20B0

其中地址為 054D20BE 的代碼正在分配記憶體,而接下來的一行將其賦予 instance ,這個時候 Singleton 的建構函式根本就還沒有被調用。

那麼問題在哪裡?如果線程調度發生在 instance 已經被賦予一個記憶體位址,而 Singleton 的建構函式還沒有被調用的微妙時刻,那麼另一個進入此函數的線程會發覺 instance 已經不為 null ,從而放心大膽的將 instance 返回並使用之。但是這個可憐的線程並不知道此時 instance 還沒有被初始化呢!

癥結在於:首先,構造一個對象不是原子操作,而是可以被打斷的;第二,更重要的,Java 允許在初始化之前就把對象的地址寫回,這就是所謂 out-of-order 。

那麼,對於 C++ 呢?典型的 C++ double-checked locking 可能是這樣的

    static Singleton* getInstDC()
    {
        if(inst_ == 0)
        {
            boost::mutex::scoped_lock l(guard_);
            if(inst_ == 0)
                inst_ = new Singleton();
        }
        return inst_;
    }

正如 Java 的行為取決於 JIT 的處理方式,C++ 程式的行為要由編譯器來決定。如果某個編譯器的處理與 JIT 類似,那麼 C++ 程式員也只好對 double-checked locking 說再見。下面是 VC7.1 在 release 配置下產生的程式碼:

    static Singleton* getInstDC()
    {
00401110  mov         eax,dword ptr fs:[00000000h]
00401116  push        0FFFFFFFFh
00401118  push        offset __ehhandler$?getInstDC@Singleton@@SAPAV1@XZ (4095F8h)
0040111D  push        eax 
        if(inst_ == 0)
0040111E  mov         eax,dword ptr [Singleton::inst_ (40D000h)]
00401123  mov         dword ptr fs:[0],esp
0040112A  sub         esp,8
0040112D  test        eax,eax
0040112F  jne         Singleton::getInstDC+6Eh (40117Eh)
        {
            boost::mutex::scoped_lock l(guard_);
00401131  mov         ecx,offset Singleton::guard_ (40D004h)
00401136  mov         dword ptr [esp],offset Singleton::guard_ (40D004h)
0040113D  call        boost::mutex::do_lock (401340h)
00401142  mov         byte ptr [esp+4],1
            if(inst_ == 0)
00401147  mov         eax,dword ptr [Singleton::inst_ (40D000h)]
0040114C  test        eax,eax
0040114E  mov         dword ptr [esp+10h],0
00401156  jne         Singleton::getInstDC+57h (401167h)
                inst_ = new Singleton();
00401158  push        1   
0040115A  call        operator new (4011A2h)
0040115F  add         esp,4
00401162  mov         dword ptr [Singleton::inst_ (40D000h)],eax
        }
00401167  mov         ecx,offset Singleton::guard_ (40D004h)
0040116C  mov         dword ptr [esp+10h],0FFFFFFFFh
00401174  call        boost::mutex::do_unlock (401360h)
        return inst_;
00401179  mov         eax,dword ptr [Singleton::inst_ (40D000h)]
    }
0040117E  mov         ecx,dword ptr [esp+8]
00401182  mov         dword ptr fs:[0],ecx
00401189  add         esp,14h
0040118C  ret             

從標記為紅色的那一句,我們看到了希望:對 inst_ 的賦值發生在 new 完成之後,這意味著至少在 VC7.1 中,我們尚且可以放心使用 double-checked locking ,儘管它未必具有可移植性。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.