【Java併發入門】02 Java記憶體模型:看Java如何解決可見性和有序性問題

大資料王小皮發表於2022-11-30

如何解決其中的可見性有序性導致的問題,這也就引出來了今天的主角——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 的語義。

參考文章:

Java記憶體模型以及happens-before規則

相關文章