2013-04-25 54 views
38

在Java中的併發實踐中,我們多次被告知,我們程序的指令可以由編譯器,JVM在運行時,甚至是處理器。所以我們應該假設執行的程序不會按照我們在源代碼中指定的順序執行完全相同的順序。java中的指令重新排序和發生之前的關係

然而,討論Java存儲器模型的最後一章提供了一個列表發生在之前的規則,指示JVM保留哪些指令排序。首先這些規則是:「程序順序規則主題中的每個動作,後來進來的程序以便線程的每一個動作之前發生」

我相信「程序順序」是指源代碼。

我的問題:假設這條規則,我想知道什麼指令可能實際上被重新排序。

「操作」被定義爲如下:

Java存儲模型的動作,其中包括讀取和寫入的變量,鎖和監視器的解鎖,以及啓動和有螺紋接合術語被指定。 JMM定義了在程序中的所有操作之前發生的部分排序。爲了保證執行動作B的線程可以看到動作A的結果(不管A和B是否出現在不同的線程中),在A和B之間的關係之前必定有一個事件發生。如果在兩個操作,JVM可以隨心所欲地對它們進行重新排序。 mentionned

其他順序規則是:

  • 監視器鎖定規則。在同一監視器鎖上的每個後續鎖之前,都會發生監視器鎖的解鎖。
  • 易變的變量規則。在每次後續讀取相同字段之前,對易失性字段進行寫入。
  • 線程啓動規則。對線程的Thread.start調用發生在已啓動線程的每個操作之前。
  • 線程終止規則。線程中的任何動作都會在任何其他線程檢測到線程已終止之前發生,要麼從Thread.join成功返回,要麼由Thread.isAlive返回false。
  • 中斷規則。另一個線程上的線程調用中斷髮生在被中斷的線程檢測到中斷之前(通過拋出InterruptedException或調用isInterrupt或中斷)。
  • 終結者規則。對象的構造函數的結束髮生在該對象的終結器開始之前。
  • 傳遞性。如果A B之前發生,和B發生之前C,則A C.

回答

43

程序順序規則的關鍵點是之前發生:在一個線程

設想這樣簡單的程序(所有變量初始爲0):

T1:

x = 5; 
y = 6; 

T2:

if (y == 6) System.out.println(x); 

從T1的角度來看,執行必​​須是其中y一致被分配在x(程序順序)之後。但是從T2的角度來看,情況並非如此,T2可能會打印0.

T1實際上允許先分配y,因爲2個分配是獨立的,並且交換它們不會影響T1的執行。

通過適當的同步,T2將始終打印5或不打印任何內容。

編輯

你似乎曲解程序命令的意思。 The program order rule boils down to

如果xy是相同的線程的動作,並且來自x之前y以程序順序,然後hb(x, y)(即x之前發生y)。

發生之前在JMM中具有非常具體的含義。特別是它而不是意味着y=6必須在壁掛鐘的角度在T1的x=5之後。這隻意味着由T1執行的動作的順序必須是,該順序與一致。您也可以參考JLS 17.4.5

應當指出的是,兩個動作之間的之前發生關係的存在並不一定意味着他們必須採取的順序發生在一個實現。如果重新排序產生與法定執行一致的結果,則不是非法的。

在這個例子中我上面給了,你會同意,從T1的視角(在單線程程序即),x=5;y=6;是符合y=6;x=5;因爲你不讀值。下一行的聲明在T1中保證可以看到這2個行爲,而不管它們的執行順序如何。

+0

謝謝。但是仍然缺少一些東西:如果程序順序規則真的存在,那麼無論「透視」(T1或T2)如何,y總是應該在T1之後的x後面賦值,不是嗎?因此T2應該總是顯示適當的x值。你提供的例子是明確的,我寧願討論程序順序規則本身的準確性(「線程中的每個動作發生在該線程中的每個動作之前,在程序順序中稍後進行」)。 – Martin 2013-05-21 11:14:58

+8

@Martin的重點是_happens-before_只是一個部分順序 - 而'x = 5' _happens-before_'y = 6'由於程序順序規則,這兩者之間根本沒有_happens-before_關係指令和T2中發生的任何事情,所以T2可以看到'y = 6'的結果,而不是'x = 5'的結果。如果'y'是易變的,那麼在T1中'y = 6'和T2中'if(y == 6)'之間的關係會產生一個_happens-before_關係,這意味着_if_ T2會看到'y = 6' _then_ it還必須看到'x = 5'。 – 2013-05-21 11:56:40