2017-06-04 61 views
0

我試圖調查在java環境中重新排序(使用JDK 9-ea + 170)的行爲,並發現我無法爲自己解釋的一件事,所以我很樂意聽到關於它的一些說明。下面是一個例子:爲什麼使用兩個volatile變量重新排序?

public class Client { 
    int x; 
    int y; 
    public void test() { 
     x++; 
     y++; 
    } 
    public static void main(String[] args) { 
     Client c = new Client(); 
     while(c.y <= c.x) new Thread(() -> c.test()).start(); 
     System.out.println(c.x + " " + c.y); 
    } 
} 

這個方案有一個test()方法,該方法只是增加x和y值。我正在創建新的線程,並調用此test(),直到某些內部java優化不會更改x++; y++;指令()的順序。這樣我證明重新排序確實發生。程序大部分時間結束(這是預期的)。 現在我已經加入volatile修飾符到Y:

public class Client { 
    int x; 
    volatile int y; 
    public void test() { 
     x++; 
     y++; 
    } 
    public static void main(String[] args) { 
     Client c = new Client(); 
     while(c.y <= c.x) new Thread(() -> c.test()).start(); 
     System.out.println(c.x + " " + c.y); 
    } 
} 

這個程序永遠不會結束,因爲揮發性保證了所有的前揮發的指令會被衝入所以x++;總是y++;之前執行的內存,這是不可能有y> x。這也是我的理解所期待的。但在那之後我已經添加揮發性到int x;過,現在我可以再次看到重新排序,從而程序結束大部分的時間:

public class Client { 
    volatile int x; 
    volatile int y; 
    public void test() { 
     x++; 
     y++; 
    } 
    public static void main(String[] args) { 
     Client c = new Client(); 
     while(c.y <= c.x) new Thread(() -> c.test()).start(); 
     System.out.println(c.x + " " + c.y); 
    } 
} 

爲什麼這裏的重新排序也發生?

回答

3

這不是重新排序的證據。實際上,volatile的結果是++不是原子的。例如,更新的一個變量(x)時考慮兩個線程(AB)以下的操作交織:

thread A: load x -> temp 
thread B: load x -> temp 
thread A: temp = temp + 1 
thread B: temp = temp + 1 
thread A: save temp -> x 
thread B: save temp -> x 

,如果你通過與交錯這些操作的工作,你會看到你在x上失去了計數。這對c.y <= c.x偶爾會失敗就足夠了。

(以下簡稱「數不清」的行爲也可能與y發生......這解釋了爲什麼這個實驗只失敗了一些時間。)

+0

能否請您提供一個例子怎麼可能是B> A? 在上面描述的流程中,賦值給A發生在賦值給B之前。 –

+0

1)變量是'x'和'y'。 'A'和'B'表示線程。 2)所有需要發生的事情是,你在'x'上的損失比在'y'上損失更多,然後'y <= x'將會是'false'。 –

+0

感謝您的更新!現在對我來說有點乾淨了。斯蒂芬,現在我運行相同的代碼,但在test()上使用同步關鍵字。它在volatile或y和x和y上工作正常,但在沒有volatile的情況下(這是預期的)失敗並且僅在x上volatile時失敗。這是正常的行爲嗎?請解釋你是否知道發生了什麼 –

相關問題