先行發生原則(Happens-Before)是Java記憶體模型定義的一個等效判斷原則,依賴這個原則,我們可以通過幾條簡單規則判斷資料是否存在競爭,執行緒是否安全,而不需要陷入Java記憶體模型苦澀難懂的定義之中,
“先行發生”原則指的是什么,比如說操作 A 先行發生于操作 B,其實就是說在操作 B 發生之前,操作 A 產生的影響能被操作 B 觀察到,“影響”包括修改了記憶體中共享變數的值、發送了訊息、呼叫了方法等,
下面是Java記憶體模型下一些“天然的”先行發生關系,這些先行發生關系無須任何同步器協助就已經存在,可以在編碼中直接使用,如果兩個操作之間的關系不在此列,并且無法從下列規則推匯出來,則這兩個操作就沒有順序性保障,虛擬機可以對它們進行重排序,
- 程式次序規則(Program Order Rule):在一個執行緒內,按照控制流順序,書寫在前面的操作先行發生于書寫在后面的操作,注意,這里說的是控制流順序而不是程式代碼順序,因為要考慮分支、回圈等結構,
- 管程鎖定規則(Monitor Lock Rule):一個 unlock 操作先行發生于后面對同一個鎖的 lock 操作, 這里必須強調的是“同一個鎖”,而“后面”是指時間上的先后,
- volatile 變數規則(Volatile Variable Rule):一個對 volatile 變數的寫操作先行發生于后面對這個 volatile 變數的讀操作,這里的“后面”同樣是指時間上的先后,
- 執行緒啟動規則(Thread Start Rule):Thread 物件的 start() 方法先行發生于此執行緒的每一個動作,
- 執行緒終止規則(Thread Termination Rule):執行緒中的所有操作都先行發生于對此執行緒的終止檢測,我們可以通過 Thread::join() 方法是否結束、Thread::isAlive() 的回傳值等手段檢測執行緒是否已經終止執行,這就算是對此執行緒的終止檢測,
- 執行緒中斷規則(Thread Interruption Rule):Thread 物件的 interrupt() 方法先行發生于被中斷執行緒的中斷檢測,我們可以通過 Thread::interrupted() 方法檢測到是否有中斷發生,這就算是被中斷執行緒的中斷檢測,
- 物件終結規則(Finalizer Rule):一個物件的初始化完成(建構式執行結束) 先行發生于它的 finalize() 方法的開始,
- 傳遞性(Transitivity):如果操作 A 先行發生于操作 B,操作 B 先行發生于操作 C,那就可以得出結論:操作 A 先行發生于操作 C,
Java 語言無須任何同步手段保障就能成立的先行發生規則有且只有上面這些,
“時間上的先后順序”與“先行發生”之間有什么不同?
- 一個操作“時間上的先發生”不代表這個操作會是 “先行發生”,譬如:操作 A 是 ,執行緒 A 對未被 volatile 修飾的共享變數的寫操作;操作 B 是,執行緒 B 對該共享變數的讀操作,在時間上,操作 A 比操作 B 先發生,但是不代表操作 A 先行發生于操作 B,
- 如果一個操作“先行發生”,也不能推匯出這個操作必定是“時間上的先發生”,譬如:程式次序規則,在一個執行緒內,按照控制流順序,書寫在前面的操作 A 先行發生于書寫在后面的操作 B,但是,不代表在時間上,操作 A 比操作 B 先發生,普通的變數會保證在方法的執行程序中,所有依賴賦值結果的地方都能獲取到正確的結果,而不能保證變數賦值操作的順序與程式代碼中的執行順序一致,
時間先后順序與先行發生原則之間基本沒有因果關系,所以我們衡量并發安全問題的時候不要受時間順序的干擾,一切必須以先行發生原則為準,
參考資料
《深入理解Java虛擬機》第 12 章:Java 記憶體模型與執行緒 12.3 Java 記憶體模型
02 | Java記憶體模型:看Java如何解決可見性和有序性問題 (geekbang.org)
本文來自博客園,作者:真正的飛魚,轉載請注明原文鏈接:https://www.cnblogs.com/feiyu2/p/17457370.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/554322.html
標籤:Java
上一篇:Simple Date Format類到底為啥不是執行緒安全的?
下一篇:返回列表
