Java內存模型與Hppens-Before規則

Java內存模型與Hppens-Before規則

爲何要有 Java內存模型?

併發編程的3個源頭問題分別是:html

  • 可見性,由緩存致使的可見性問題
  • 有序性,由編譯優化致使的有序性問題
  • 原子性,由線程切換致使的原子性問題

Java內存模型就是爲了解決可見性和有序性問題。java

什麼是 Java 內存模型?

緩存會致使可見性問題,編譯優化會致使有序性問題。若是要避免這兩個問題,最簡單的方法不就是禁用緩存和編譯優化。這樣就丟掉了優化程序性能的有利武器,顯然是不可取的。程序員

合理的方案應該是按需禁用緩存以及編譯優化。什麼叫按需禁用緩存以及編譯優化呢?指的就是程序員在寫代碼的過程當中,對有可能出現併發問題的代碼禁用緩存和編譯優化。編程

Java內存模型就是禁用緩存和編譯優化的一種規範,它規範了 JVM 如何提供按需禁用緩存和編譯優化的方法。如今提到的 Java 內存模型,通常指的是 JDK 5 開始使用內存模型,遵循的是 JSR-133 描述的規範。緩存

JSR133

Java內存模型是一個雄心勃勃的計劃,它是編程語言規範第一次嘗試合併一個可以在各類處理器架構中爲併發提供一致語義的內存模型。不過,定義一個既一致又直觀的內存模型遠比想象要更難。JSR133 爲Java語言定義了一個新的內存模型,它修復了早期內存模型中的缺陷。爲了實現 JSR133,final和volatile的語義須要從新定義。架構

完整的語義見:http://www.cs.umd.edu/users/p...,可是正式的語義不是當心翼翼的,它是使人驚訝和清醒的,目的是讓人意識到一些看似簡單的概念(如同步)其實有多複雜。幸運的是,你不須要懂得這些正式語義的細節——JSR133的目的是建立一組正式語義,這些正式語義提供了volatile、synchronzied和final如何工做的直觀框架。併發

—— Java內存模型FAQ(三)JSR133是什麼?app

Java內存模型主要分紅兩部分,一部分面向併發編程的開發人員,一部分面向JVM開發人員,咱們須要關注的是前者。前者主要包括 volatilesynchronizedfinal 三個關鍵字,以及六項 Happens-Before 規則。框架

Happens-Before 規則

Java內存模型是按需禁用緩存和編譯優化的規則。Happens-Before 表示前面一個操做對後面一個操做是可見的,它約束了編譯器的優化行爲,編譯器能夠優化,可是須要遵循 Happens-Before 規則。編程語言

有這樣 6 條和可見性相關的規範:

  • 程序的順序性規則:按照程序順序,前面的操做 Happens-before 於後面的操做。
  • volatile 變量規則:對 volatile 變量的寫操做,Happens-before 於對該 volatile 變量的讀操做。
  • 傳遞性:若 A Happens-Before B,B Happens-Before C,則 A Happens-Before C。
  • 管程中的鎖規則:對一個鎖的解鎖 Happens-Before 於後續對這個鎖的加鎖。
  • 線程 start() 規則:主線程 A 啓動子線程 B,則子線程 B 能看到主線程 A 在啓動子線程 B 以前的操做。
  • 線程 join() 規則:主線程 A 等待子線程 B 完成後,主線程能看到子線程 B 操做

Happens-Before規則對 volatile 語義的加強

前面提到過,如今所說的 Java 內存模型通常指的是 Java1.5 以後的內存模型,它遵循 JSR-133 描述的規範。採用新的規範的緣由是舊的 Java 內存模型存在問題,好比:對 final 修飾的變量進行過分的編譯優化,以及 volatile 的語義問題。

// 如下代碼來源於【參考1】
class VolatileExample {
  int x = 0;
  volatile boolean v = false;
  public void writer() {
    x = 42;
    v = true;
  }
  public void reader() {
    if (v == true) {
      // 這裏x會是多少呢?
    }
  }
}

上面這段代碼,變量 v 是一個 volatile 類型的變量,則會禁用緩存,全部線程對 v 的操做都是直接從內存中讀取。此時,線程 A 執行 writer() 方法,則 v = true 會被寫入內存;同時線程 B 執行 reader() 方法,顯然會經過 if 判斷,那麼此時 x 的值會是多少?

在 Java1.5 以前,x 的值多是 0,也有多是 42。由於 x 有可能被 CPU 緩存起來,致使可見性問題。這顯然不是咱們指望的狀況,因此在新的 Java 內存模型中對 volatile 的語音進行了加強,就是依靠 Happens-Before 中的 volatile 變量規則和傳遞性規則。

volatile語義加強

如上圖所示,線程 A 執行 writer() 方法,線程 B 執行 reader() 方法:

  • 根據順序性規則,x=42 happens-before 於寫變量 v;讀變量 v happens-before 於讀變量 x
  • 根據volatile變量規則,對 volatile 變量的寫操做 happens-before 於對該變量的讀操做
  • 根據傳遞性規則,x=42 happens-before 於讀變量 x

此時,經過 Happens-Before 規則對 volatile 變量的語義加強,線程 B 讀到的 x 就必定是 42,而不會存在是 0 的狀況,符合咱們對程序的期待。

相關文章

Java內存模型FAQ

JSR 133 (Java Memory Model) FAQ

再有人問你Java內存模型是什麼,就把這篇文章發給他。

02 | Java內存模型:看Java如何解決可見性和有序性問題

Java內存模型(JMM)詳解

Java內存模型相關原則詳解

相關文章
相關標籤/搜索