2013-02-22 148 views
0

在Java中,C和C++是保證在單個給定線程內按順序逐行執行的源代碼,即使在編譯器優化後也是如此。好像沒有什麼是不可能獲得成功,如果系統被允許重新整理你的代碼,但我似乎無法找到任何文件,以保證如果我在Java中的以下內容:在單個線程內執行Java代碼是否保證順序?

class MyClass{ 
String testString = ""; 

public MyClass(){ 

} 

public void foo(){ 
    testString = "foo"; 
} 
public void bar(){ 
    testString = "bar"; 
    testString += "r"; 
} 
public String getTestString(){ 
    return testString; 
} 

} 

class Main{ 
static void main(String[] args){ 
    MyClass testClass = new MyClass(); 
    testClass.foo(); 
    System.out.println(class.getTestString()); 
    testClass.bar(); 
    System.out.println(class.getTestString()); 
} 
} 

輸出將永遠是

"foo" 
"barr" 

和從未

"foo" 
"rbar" 

,或者如果內它們的方法調用和語句不是順序地執行作爲SPE可能出現的任何其它可能的變化在源代碼中引用。

這個問題引發了Java的具體問題,因爲它使得程序員對目標系統上的字節碼編譯器和JIT編譯器或解釋器將對您的代碼執行的操作的控制明顯減少。我所討論的主要系統是Android,爲此我實現了自己的信號量和互斥鎖定機制(例如,不會過多地使用像'synchronized'和'volatile'這樣的內置Java併發機制,它更適合於我的應用程序比Java提供的應用程序。然而,一位朋友警告我說,由於Java從源代碼到機器代碼的多重轉換水平,除非我使用Java的內置併發機制,否則不能保證我的信號量和鎖定實現將按我的意圖執行。這真的歸結爲是否存在一個特定的保證,對於任何給定的運行時實現,代碼的執行將在單個線程內連續執行。所以主要的問題是:

  1. 在C和C + +是代碼執行保證順序,儘管編譯器優化?如果沒有,是否足以實現這樣的保證來禁用編譯器優化?

  2. 在Java中,儘管潛在地改變了字節碼編譯器和JIT編譯器或解釋器(專門在Android上運行,但也適用於任意虛擬機實現),但是代碼執行是保證順序的?

  3. 如果上面的答案是肯定的,那麼是否有任何編程語言/平臺/上下文在單線程中的順序執行不能保證?

+3

'在C和C++是執行代碼保證是,儘管編譯順序優化?'你在那裏開放了一整罐蠕蟲... – BoBTFish 2013-02-22 15:48:58

+6

絕對不是。 [編譯器很少編譯你寫的代碼](http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-1-of-2)。 – 2013-02-22 15:49:16

+0

在單線程的過程中,您的方案將永遠不會發生...... – 2013-02-22 15:50:26

回答

3

如果只有一個線程,您的代碼將具有直觀的預期結果。任何優化必須在優化之前保留功能。意想不到的事情只有當你有多個線程時纔會發生。

基本符合當有多個線程,你的問題是關於如這裏所定義的「線程內語義」即出現反直覺的行爲

Java語言規範的交易:

存儲模式決定了什麼樣的價值觀可以在程序的每個點上讀取。隔離的每個線程的行爲必須像該線程的語義一樣行爲,除了每次讀取的值由內存模型決定。當我們提到這個時,我們說程序服從於線程內部的語義。線程內語義是單線程程序的語義,並且允許基於線程內讀取操作所看到的值完全預測線程的行爲。爲了確定執行過程中線程t的動作是否合法,我們簡單地評估線程t的實現,因爲它將在單線程上下文中執行,如本規範其餘部分所定義。

http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html

1

是的,你上面提供的代碼將被按順序執行。即使有50個處理器,您的代碼也會在一個線程上按順序執行。

順便說一句,很確定你不能調用一個變量類。即使你可以,也不要。

+0

呵呵,我的不好。在會議開始前的最後幾秒鐘就剔除了這個例子 – CCJ 2013-02-22 19:20:35

2

即使在沒有螺紋大多數編譯器被允許重新排序的代碼,以獲得最佳的速度,找到更多的答案。但重新排序的結果不允許影響結果。在C/C++中,這被稱爲as-if規則。

因此,在函數內,只要結果不受影響,編譯器就可以重新排序進行優化。所以我們在「源代碼」和「生成的代碼」之間略有不同。

在線程代碼中會有一個線程執行一個函數嗎?這取決於你如何定義線程。什麼是線程(一組堆棧幀/寄存器/和當前執行點)。

我看到移動核心之間的線程沒有問題(雖然我可以看到爲什麼運行時不會想這樣做,我不認爲它是強制性的)。所以你不能假設線程不會跳轉內核。但是,如果您認爲「執行線程」是當前狀態而不涉及任何硬件。

你可以保證的是,「執行線程」將從一段「生成的代碼」的開始到結束執行。它不會遺漏任何「生成的代碼」。它將按順序執行「生成的代碼」。如果不計劃並重新計劃,它將從其離開的地方繼續。

1

我認爲最簡單的答案是編譯器開發人員遵循「不應該修改單線程程序的行爲」的口頭禪,除此之外,我認爲你是你自己的。

「preshing on programming」對我遇到的無序執行問題有一些最好的解釋。如果有人知道我想知道的一組更好的文章。這裏有兩個人,但如果你真的想鑽研它,你應該檢查出更多的材料在網站上:

Memory Ordering at Compile Time
Weak vs. Strong Memory Models