[英]Java volatile and happens-before scope
教程http://tutorials.jenkov.com/java-concurrency/volatile.html說
如果讀取/寫入最初發生在對 volatile 變量的寫入之前,則對其他變量的讀取和寫入不能重新排序以在寫入 volatile 變量之后發生。 寫入 volatile 變量之前的讀取/寫入保證“發生在”寫入 volatile 變量之前。
“在寫入 volatile 變量之前”是什么意思? 這是否意味着以前以我們正在寫入 volatile 變量的相同方法進行讀/寫? 還是更大的 scope (也在調用堆棧更高的方法中)?
JVM 可以重新排序操作。 例如,如果我們有i
, j
變量和代碼
i = 1;
j = 2;
JVM 可以以重新排序的方式運行它
j = 2;
i = 1;
但是如果j
變量標記為volatile
則 JVM 僅運行操作
i = 1;
j = 2;
寫入i
“發生在寫入 volatile 變量之前” j
。
JVM 確保對 volatile 變量的寫入發生在對其進行任何讀取之前。 取兩條線。 保證對於單個線程,執行遵循as-if-serial語義。 基本上,您可以假設在同一個線程中存在隱式的先發生關系 b/w 兩次執行(編譯器仍然可以自由地重新排序指令)。 基本上,單個線程有一個總順序 b/w 它的指令由發生前的關系控制。
多線程程序有許多這樣的部分順序(每個線程在本地指令集中都有一個總順序,但在線程之間沒有全局順序),但沒有全局指令集的總順序。 同步就是為您的程序提供盡可能多的總順序。
回到 volatile 變量,當線程從其中讀取時,JVM 確保所有對它的寫入都發生在讀取之前。 現在由於這個順序,寫入線程在寫入變量之前所做的一切對從變量讀取的線程都是可見的。 所以是的,要回答你的問題,即使調用堆棧中的變量也應該對讀取線程可見。
我會試着畫一張視覺圖。 這兩個線程可以想象成兩個平行的軌道,寫入一個 volatile 變量可以是它們的睡眠者之一。 你基本上得到一個
A -----
|
|
------- B
形成了兩個執行線程的總順序。 由於這個總順序,在睡眠者之前的 A 中的所有內容都應該在睡眠者之后對 B 可見。
JMM 是根據發生在關系之前定義的,我們稱之為->
。 如果a->b
,那么b
應該看到 a 的所有a
。 這意味着對重新排序加載/存儲有限制。
如果a
是 volatile 寫入, b
是同一變量的后續 volatile 讀取,則a->b
。 這稱為易失性變量規則。
如果代碼中a
出現在b
之前,則a->b
。 這稱為節目順序規則。
如果a->b
和b->c
,那么a->c
。 這稱為傳遞性規則。
因此,讓我們將其應用於一個簡單的示例:
int a;
volatile int b;
thread1(){
a=1;
b=1
}
thread2(){
int rb=b;
int ra=a;
if(rb==1 and ra==0) print("violation");
}
所以問題是如果thread2看到rb=1,它會看到ra=1嗎?
a=1->b=1
由於程序順序規則。
由於 volatile 變量規則, b=1->rb=b
(因為我們看到值 1)。
rb=b->ra=a
由於程序順序規則。
現在我們可以應用傳遞性規則兩次,我們可以得出結論a=1->ra=a
。 因此 ra 需要為 1。
這意味着:
a=1
和b=1
不能重新排序。rb=b
和ra=a
不能重新排序否則我們可能會得到一個rb=1
和ra=0
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.