同步和Java記憶體模型(四)有序性
作者:Doug lea 譯者:杜建雄校對者:歐振聰,方騰飛
有序性
有序性規則表現在以下兩種場景: 執行緒內和執行緒間
- 從某個執行緒的角度看方法的執行,指令會按照一種叫“序列”(as-if-serial)的方式執行,此種方式已經應用於順序程式語言。
- 這個執行緒“觀察”到其他執行緒併發地執行非同步的程式碼時,任何程式碼都有可能交叉執行。唯一起作用的約束是:對於同步方法,同步塊以及volatile欄位的操作仍維持相對有序。
再次提醒,這些僅是最小特性的規則。具體到任何一個程式或平臺上,可能存在更嚴格的有序性規則。所以你不能依賴它們,因為即使你的程式碼遵循了這些更嚴格的規則,仍可能在不同特性的JVM上執行失敗,而且測試非常困難。
需要注意的是,執行緒內部的觀察視角被JLS [1] 中其他的語義的討論所採用。例如,算術表示式的計算線上程內看來是從左到右地執行操作(JLS 15.6章節),而這種執行效果是沒有必要被其他執行緒觀察到的。
僅當某一時刻只有一個執行緒操作變數時,執行緒內的執行表現為序列。出現上述情景,可能是因為使用了同步,互斥體[2] 或者純屬巧合。當多執行緒同時執行在非同步的程式碼裡進行公用欄位的讀寫時,會形成一種執行模式。在這種模式下,程式碼會任意交叉執行,原子性和可見性會失效,以及產生競態條件。這時執行緒執行不再表現為序列。
儘管JLS列出了一些特定的合法和非法的重排序,如果碰到所列範圍之外的問題,會降低以下這條實踐保證 :執行結果反映了幾乎所有的重排序產生的程式碼交叉執行的情況。所以,沒必要去探究這些程式碼的有序性。
譯註:
【2】互斥體:原文為structural exclusion,譯者認為意同 mutual exclusion ,詳見 互斥體。
原文
Synchronization and the Java Memory Model
Ordering
Ordering rules fall under two cases, within-thread and between-thread:
- From the point of view of the thread performing the actions in a method, instructions proceed in the normal as-if-serial manner that applies in sequential programming languages.
- From the point of view of other threads that might be “spying” on this thread by concurrently running unsynchronized methods, almost anything can happen. The only useful constraint is that the relative orderings of synchronized methods and blocks, as well as operations on volatile fields, are always preserved.
Again, these are only the minimal guaranteed properties. In any given program or platform, you may find stricter orderings. But you cannot rely on them, and you may find it difficult to test for code that would fail on JVM implementations that have different properties but still conform to the rules.
Note that the within-thread point of view is implicitly adopted in all other discussions of semantics in JLS. For example, arithmetic expression evaluation is performed in left-to-right order (JLS section 15.6) as viewed by the thread performing the operations, but not necessarily as viewed by other threads.
The within-thread as-if-serial property is helpful only when only one thread at a time is manipulating variables, due to synchronization, structural exclusion, or pure chance. When multiple threads are all running unsynchronized code that reads and writes common fields, then arbitrary interleavings, atomicity failures, race conditions, and visibility failures may result in execution patterns that make the notion of as-if-serial just about meaningless with respect to any given thread.
Even though JLS addresses some particular legal and illegal reorderings that can occur, interactions with these other issues reduce practical guarantees to saying that the results may reflect just about any possible interleaving of just about any possible reordering. So there is no point in trying to reason about the ordering properties of such code.