如何解決其中的可見性和有序性導致的問題,這也就引出來了今天的主角——Java 記憶體模型。 一、什麼是 Java 記憶體模型? 導致可見性的原因是緩存,導致有序性的原因是編譯優化,那解決可見性、有序性最直接的辦法就是禁用緩存和編譯優化,但這樣雖然解決了問題,但也導致帶來的性能優化都沒了。 因此,解決方案 ...
如何解決其中的可見性和有序性導致的問題,這也就引出來了今天的主角——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 的語義。
參考文章: