[英]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.