發生在關係之前
(以下是 Java 語言規範所說的簡化版本。為了更深入地理解,你需要閱讀規範本身。)
發生之前 - 關係是記憶體模型的一部分,它允許我們理解和推理記憶體可見性。正如 JLS 所說( JLS 17.4.5 ):
“兩個動作可以通過先發生關係來排序。如果一個動作發生在另一個動作之前,那麼第一個動作在第二個動作之前可見並在第二個之前被命令。”
這是什麼意思?
操作
上述引用所引用的操作在 JLS 17.4.2 中指定。規範中列出了 5 種列出的操作:
-
閱讀:讀取非易失性變數。
-
寫:寫一個非易失性變數。
-
同步動作:
-
易失性讀取:讀取易失性變數。
-
易失寫:寫一個 volatile 變數。
-
鎖。鎖定顯示器
-
開鎖。解鎖顯示器。
-
執行緒的(合成)第一個和最後一個動作。
-
啟動執行緒或檢測執行緒已終止的操作。
-
-
外部行動。具有取決於程式所在環境的結果的操作。
-
執行緒分歧行動。這些模擬了某些無限迴圈的行為。
程式順序和同步順序
這兩個排序( JLS 17.4.3 和 JLS 17.4.4 )控制 Java 中語句的執行
程式順序描述了單個執行緒中語句執行的順序。
同步順序描述了由同步連線的兩個語句的語句執行順序:
-
監視器上的解鎖操作與該監視器上的所有後續鎖定操作同步。
-
對 volatile 變數的寫入與任何執行緒對同一變數的所有後續讀取同步。
-
啟動執行緒的動作(即對
Thread.start()
的呼叫) 與它啟動的執行緒中的第一個動作同步 (即呼叫執行緒的run()
方法)。 -
欄位的預設初始化與每個執行緒中的第一個操作同步。 (有關此問題的解釋,請參閱 JLS。)
-
執行緒中的最後一個操作與另一個檢測終止的執行緒中的任何操作同步。例如返回
join()
呼叫或isTerminated()
呼叫返回true
。 -
如果一個執行緒中斷另一個執行緒,則第一個執行緒中的中斷呼叫與另一個執行緒檢測到執行緒被中斷的點同步。
發生在訂單之前
這種排序( JLS 17.4.5 )決定了是否保證儲存器寫入對後續儲存器讀取可見。
更具體地說,變數 v
的讀取保證觀察對 v
的寫入,當且僅當 write(v)
發生時 - 在 read(v)
之前並且沒有插入到 v
的寫入。如果有干預寫入,則 read(v)
可能會看到它們的結果而不是之前的結果。
定義事先排序的規則如下:
-
發生在規則#1 之前 - 如果 x 和 y 是同一個執行緒的動作,並且 x 在程式順序中出現在 y 之前,則 x 發生在 y 之前。
-
發生在規則#2 之前 - 從物件的建構函式的末尾到該物件的終結器的開始有一個發生在前的邊緣。
-
發生在規則#3 之前 - 如果動作 x 與後續動作 y 同步,則 x 發生在 y 之前。
-
發生在規則#4 之前 - 如果 x 發生 - 在 y 和 y 發生之前 - 在 z 之前然後 x 發生在 z 之前。
此外,Java 標準庫中的各種類被指定為定義發生在之前的關係。你可以將其解釋為意味著它以某種方式發生,而無需確切知道如何滿足保證。