如何解決其中的可見性和有序性導致的問題,這也就引出來了今天的主角——Java 記憶體模型。
一、什麼是 Java 記憶體模型?
導致可見性的原因是快取,導致有序性的原因是編譯最佳化,那解決可見性、有序性最直接的辦法就是禁用快取和編譯最佳化,但這樣雖然解決了問題,但也導致帶來的效能最佳化都沒了。
因此,解決方案是:提出一套規則和方法,是程式設計師能在該禁用的時候禁用,不該禁用的時候不禁用。
Java 記憶體模型規範就是來解決這個問題的 —— 提供按需禁用快取和編譯最佳化的方法
具體來說,這些方法包括 volatile、synchronized 和 final 三個關鍵字,以及六項 Happens-Before 規則,這也正是本期的重點內容。
二、Happens-Before 規則
Q:如何理解 Happens-Before 呢?
A:前面一個操作的結果對後續操作是可見的。但不能理解為前一個操作發生在後續操作的前面。
只要最終語義是對的,編譯器怎麼最佳化都行。
1、程式的順序性規則
這條規則是指在一個執行緒中,按照程式順序,前面的操作 Happens-Before 於後續的任意操作。
2、volatile 變數規則
這條規則是指對一個 volatile 變數的寫操作, Happens-Before 於後續對這個 volatile 變數的讀操作。
3、傳遞性
這條規則是指如果 A Happens-Before B,且 B Happens-Before C,那麼 A Happens-Before C。
4、管程中鎖的規則
這條規則是指對一個鎖的解鎖 Happens-Before 於後續對這個鎖的加鎖。
5、執行緒 start() 規則
這條是關於執行緒啟動的。它是指主執行緒 A 啟動子執行緒 B 後,子執行緒 B 能夠看到主執行緒在啟動子執行緒 B 前的操作。
6、執行緒 join() 規則
它是指主執行緒 A 等待子執行緒 B 完成(主執行緒 A 透過呼叫子執行緒 B 的 join() 方法實現),當子執行緒 B 完成後(主執行緒 A 中 join() 方法返回),主執行緒能夠看到子執行緒的操作。當然所謂的“看到”,指的是對共享變數的操作。
疑惑
Q:volatile、synchronized 和 final 能理解是提供給程式設計師用的,六項 Happens-Before 規則是約束誰的呢?
A:這是給程式設計師的保障,按照提供的規則寫,就能保證 Happens-Before 的語義。
參考文章: