簡體   English   中英

發生之前和重新排列揮發物

[英]Happens-before and reordering of volatile

有多個代碼示例假定以下說明(1)(2)不能重新排序:

int value;
volatile boolean ready;

// ...

value = 1;     // (1)
ready = true;  // (2)

后面的堆棧溢出答案參考JLS§17.4.5

如果x和y是同一線程的動作,並且x按程序順序位於y之前,則hb(x,y)。

但是我不明白為什么這應該在這里適用,因為JLS 示例17.4-1還指出:

允許編譯器在不影響單獨執行該線程的情況下,對每個線程中的指令重新排序。

這顯然是事實。

JLS中針對volatile所有其他定義僅針對同一volatile變量,而不針對其他動作:

在隨后每次對該字段進行讀取之前,都會寫入一個易失字段(第8.3.1.4節)。


這使我感到困惑,因為人們看到了對保證不重新排列易失性(讀或寫)的使用的保證。

您能否以JLS或其他基於JLS的資料為基礎進行解釋。

孤立地,您的代碼不能保證任何事情。 這里涉及第二個線程, 我們也需要它的代碼 您鏈接的教程同時顯示兩個線程是有原因的。

如果兩個線程的代碼是這樣的:

int value;
volatile boolean ready;

// Thread - 1
value = 1;     // (1)
ready = true;  // (2)

// Thread - 2
if (ready) {  // (3)
    x = value // (4)
}

然后,由於程序順序,我們在(1)和(2)之間有一個事前發生的關系:

如果x和y是同一線程的動作,並且x按程序順序位於y之前,則hb(x,y)。

並且由於ready易失,我們在(2)和(3)之間具有先發生后關系:

在隨后每次對該字段進行讀取之前,都會寫入一個易失字段(第8.3.1.4節)。

再次由於程序順序,我們在(3)和(4)之間具有事前發生的關系:

如果x和y是同一線程的動作,並且x按程序順序位於y之前,則hb(x,y)。

因此,在鏈(1)→(2),(2)→(3),(3)→(4)之前發生了

並且由於事前發生是傳遞關系(如果A發生在B之前,B發生在C之前,那么A發生在C之前),這意味着(1)發生在(4)之前。

如果翻轉(3)和(4),以便第二個線程在讀取ready之前先讀取value ,那么在鏈發生之前,before-before會中斷,並且我們再也無法獲得對value的讀取的保證。

這是一個不錯的教程 ,其中包含更多JMM陷阱。

Java內存模型不是很有趣嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM