2017-09-16 73 views
1

下閱讀對象的字段這個職位是閱讀後提出:https://shipilev.net/blog/2016/close-encounters-of-jmm-kind/#pitfall-semi-sync閱讀對象的引用和JMM

class Box { 
    int x; 
    public Box(int v) { 
    x = v; 
    } 
} 

class RacyBoxy { 
    Box box; 

    public synchronized void set(Box v) { 
    box = v; 
    } 

    public Box get() { 
    return box; 
    } 
} 

和測試:

@JCStressTest 
@State 
public class SynchronizedPublish { 
    RacyBoxy boxie = new RacyBoxy(); 

    @Actor 
    void actor() { 
    boxie.set(new Box(42)); // set is synchronized 
    } 

    @Actor 
    void observer(IntResult1 r) { 
    Box t = boxie.get(); // get is not synchronized 
    if (t != null) { 
     r.r1 = t.x; 
    } else { 
     r.r1 = -1; 
    } 
    } 
} 

筆者說,這是可能的,r.r1 == 0 。我同意 那。但是,我對一個解釋感到困惑:

實際的故障來自事實,即讀取對象的引用並讀取對象的字段在內存模型下是不同的。

我同意

閱讀對象的引用和閱讀對象的字段是內存模型 下不同,但我不知道它是如何對結果產生影響。

請幫我理解它。

P.S.如果有人對@Actor感到困惑。它只是意味着:在一個線程中運行。

回答

1

我認爲它講述了閱讀有關順序整合的代碼的人們的一個常見的miconception。對一個實例的引用在一個線程中可用這一事實並不意味着它的構造函數已被設置。換句話說:讀取實例與讀取實例字段的操作不同。許多人認爲,一旦他們可以觀察一個實例,它需要運行構造函數,但由於缺少讀取同步,上述示例並不適用。

+0

那麼,你的意思是? 第一個線程(actor)發佈了引用,但對象沒有初始化是。然後,觀察者讀取box('boxie.get()')的引用,並且引用不是'null',觀察者讀取't.x == 0'。 – Gilgamesz

+0

是的,發佈實例並調用其構造函數並不總是原子的,除非您使對象安全發佈。 –

1

生病稍稍這裏增加接受的答案 - 沒有一些障礙是完全無法保證一旦你看到一個參考(覺得有些線程可以得到一個參考的持有) - 所有這一切構造領域被初始化。如果我沒有弄錯的話,我實際上已經在前一段時間回答了你的一個問題。

在構造函數的最後字段LoadLoad和​​之間插入兩個障礙;它,你想想自己 - 你會發現,沒有任何操作後的構造函數可以被重新排序與一個它:

Load -> Load (no Load can be re-ordered with a previous Load) 
Load -> Store (no Store can be re-ordered with a previous Load) 

另外請注意,這將是不可能爲你打破在目前的x86內存模型下 - 因爲它是(太?)強大的內存模型;因此這些障礙是免費x86 - 它們根本不被插入,因爲操作不會被重新排序。

+0

感謝@Eugene的額外觀點。 – Gilgamesz