2014-10-28 67 views
6

在約http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html底部,它說:爲什麼在雙重檢查鎖定中不可變對象是安全的?

雙檢鎖不可變對象

如果Helper是不可變對象,使得所有助手的字段是決賽,然後雙擊 - 檢查鎖定將無需使用易失性字段。這個想法是,對一個不可變對象(比如一個String或一個Integer)的引用應該和int或者float類似。讀取和寫入對不可變對象的引用是原子的。

樣品和一個可變的解釋如下:

// Broken multithreaded version 
// "Double-Checked Locking" idiom 
class Foo { 
    private Helper helper = null; 
    public Helper getHelper() { 
    if (helper == null) 
     synchronized(this) { 
     if (helper == null) 
      helper = new Helper(); 
     }  
    return helper; 
    } 
    // other functions and members... 
    } 

第一個原因,它不工作

最明顯的原因,它不工作它,初始化助手對象和寫入助手字段的寫入操作可以完成或不按順序感知。因此,調用getHelper()的線程可以看到對助手對象的非空引用,但請參閱助手對象的字段的默認值,而不是在構造函數中設置的值。

如果編譯器內聯調用構造函數,那麼如果編譯器可以證明構造函數不能拋出異常或執行同步,則初始化對象和寫入輔助對象字段的寫入操作可以自由重新排序。

即使編譯器沒有對這些寫入重新排序,在多處理器上,處理器或內存系統可能會重新排列這些寫入,如在另一個處理器上運行的線程所感知的那樣。

我的問題是:爲什麼不可變的類沒有問題?我無法看到重新排序與班級是否可變的任何關係。

謝謝

回答

0

不可變類確實有問題。你引用的部分是在JSR133中對Java內存進行了更改。

具體而言,影響不可變對象的更改與對final關鍵字所做的某些更改有關。結帳http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#finalRight

最重要的部分是:

爲對象的最終字段的值在構造函數中設置。假設對象構造「正確」,一旦構造對象,分配給構造器中最終字段的值將對所有其他線程都可見而不同步。

1

之所以代碼是「破」了通常的對象是helper可能是非零而是指向尚未對象在你的報價解釋完全初始化尚未。

但是,如果Helper類是不可變的,這意味着所有的字段都最終,the Java Memory Model保證他們安全地發佈即使提供對象通過數據種族(這是你的榜樣的情況下):

final字段還允許程序員在沒有同步的情況下實現線程安全的不可變對象。 線程安全的不可變對象被所有線程視爲不可變,即使使用數據競爭將線程的引用傳遞給線程間的不可變對象。這可以提供安全保證,防止錯誤或惡意代碼濫用不可變類。必須正確使用final字段才能提供不變性保證。

+0

我認爲你的術語「完全初始化」意味着與你的鏈接中的東西有所不同? 「一個對象在其構造函數完成時被認爲是完全初始化的,一個線程只能在該對象被完全初始化後才能看到對象的引用,保證能夠看到該對象最終字段的正確初始化值。 – 2014-10-29 01:56:55

+0

不,它是相同的:在調用'new Helper()'之後,JMM保證final字段已經被初始化,但沒有說可能或未被初始化的常規字段。 – assylias 2014-10-29 07:03:23

相關問題