![](/img/trans.png)
[英]Volatile happens-before relationship when there's mix of volatile and non-volatile fields
[英]Happens-before relationships with volatile fields and synchronized blocks in Java - and their impact on non-volatile variables?
我對線程概念還很陌生,並嘗試更多地了解它。 最近,我發現了一篇關於Jeremy Manson撰寫的“ Java中的易失手段”的博客文章,他寫道:
當一個線程寫入一個volatile變量,而另一個線程看到該寫入時,第一個線程告訴第二個線程關於內存的所有內容,直到它執行對該volatile變量的寫入。 [...]線程1在寫入
[volatile] ready
之前看到的所有內存內容必須對線程2可見,在讀取值為true
后才ready
。 [自己強調]
現在,這是否意味着在寫入volatile變量時,線程1的內存中保存的所有變量(volatile或not)將在讀取volatile變量后變為可見? 如果是這樣, 是否有可能從官方Java文檔/ Oracle源代碼中將該語句拼湊起來? 從哪個版本的Java開始這個工作?
特別是,如果所有線程共享以下類變量:
private String s = "running";
private volatile boolean b = false;
並且線程1首先執行以下操作:
s = "done";
b = true;
然后線程2執行(在線程1寫入volatile字段之后):
boolean flag = b; //read from volatile
System.out.println(s);
這會保證打印“完成”嗎?
如果不將b
聲明為volatile
而是將寫入和讀取放入synchronized
塊,會發生什么?
另外,在題為“ 線程之間是否共享靜態變量? ”的討論中,@ TREE 寫道 :
不要使用volatile來保護多個共享狀態。
為什么? (對不起;我還沒有就其他問題發表評論,或者我會在那里問過......)
是的,保證線程2將打印“完成”。 當然,這是因為線程1中對b
的寫入實際上發生在線程2中從b
讀取之前,而不是在同一時間或之前發生!
這里推理的核心是先發生過的關系 。 多線程程序執行被視為由事件組成。 事件可以通過發生在之前的關系來關聯,這表示一個事件發生在另一個事件之前。 即使兩個事件沒有直接關聯,如果你可以追蹤從一個事件到另一個事件的一系列發生在前的關系,那么你可以說一個事件發生在另一個事件之前。
在您的情況下,您有以下事件:
s
b
b
讀取 s
讀取 以下規則發揮作用:
以下情況發生在 - 因此存在關系之前:
s
發生在線程1寫入b
(程序順序規則)之前 b
發生在線程2從b
讀取之前(易失性規則) b
讀取在線程2從s
讀取之前(程序順序規則) 如果您遵循該鏈,您可以看到結果:
s
發生在線程2從s
讀取之前 如果不將b聲明為volatile,而是將寫入和讀取放入同步塊,會發生什么?
當且僅當您使用相同的鎖保護所有此類同步塊時,您才能獲得與volatile
示例相同的可見性保證。 此外,您還將相互排除此類同步塊的執行。
不要使用volatile來保護多個共享狀態。
為什么?
volatile
不保證原子性:在你的例子中,在你顯示的寫入之后, s
變量也可能被其他線程變異; 閱讀線程無法保證它看到的是哪個值。 在讀取volatile
之后但在讀取s
之前寫入s
發生同樣的事情。
在實踐中做什么是安全的,並且在寫入volatile
變量的引用中可以傳遞可共享的不可變狀態 。 所以也許這就是“一個共享國家”的意思。
是否可以從官方Java文檔/ Oracle源代碼中將該語句拼湊起來?
來自規范的行情:
17.4.4。 同步順序
對易失性變量v(第8.3.1.4節)的寫入與任何線程對v的所有后續讀取同步(其中“后續”根據同步順序定義)。
17.4.5。 發生在訂單之前
如果x和y是同一個線程的動作,並且x在程序順序中出現在y之前,那么hb(x,y)。
如果動作x與后續動作y同步,那么我們也有hb(x,y)。
這應該足夠了。
從哪個版本的Java開始這個工作?
Java語言規范,第3版引入了內存模型規范的重寫,這是上述保證的關鍵。 NB大多數以前的版本都表現得好像保證在那里,而且許多代碼行實際上依賴於它。 當人們發現保證實際上沒有存在時,人們感到很驚訝。
這會保證打印“完成”嗎?
如Java Concurrency in Practice中所述 :
當線程
A
寫入volatile
變量並且隨后線程B
讀取相同的變量時, 在寫入volatile
變量之前,A可見的所有變量的值在讀取volatile
變量后變為B可見 。
是的 ,這保證了打印“完成”。
如果不將b聲明為volatile,而是將寫入和讀取放入同步塊,會發生什么?
這也將保證相同。
不要使用volatile來保護多個共享狀態。
為什么?
因為,volatile只保證可見性。 它不保證原子性。 如果我們在一個被線程A
訪問的方法中有兩個易失性寫入而另一個線程B
正在訪問那些易失性變量,那么當線程A
正在執行該方法時,線程A
可能會被線程B
搶占。操作的中間(例如,在第一次易失性寫入之后但在線程A
第二次易失性寫入之前)。 因此,保證操作synchronization
的原子性是最可行的出路。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.