對 Java 內存模型的理解

Java 內存模型

Java內存模型規定了在多線程程序中,什麼樣的行爲是容許出現的,什麼樣的行爲是禁止出現的。這樣說可能有點抽象,咱們換一個角度。將程序行爲抽象成讀操做和寫操做,每一個線程有本身的局部變量,同時線程之間還存在共享變量。那麼一個多線程程序執行結束後,全部變量會有一個最終值。Java內存模型來決定什麼樣的值合法,什麼樣的值不合法。html

內存模型不能要求的太嚴格,這樣會阻礙不少優化方法,下降程序執行的效率,但也不能要求的太鬆,由於這樣會致使一些執行結果違反咱們的直覺。例如指令間的重排序問題,若是線程內部的指令徹底按照程序中指明的次序執行,而且每次執行一條指令,執行的結果當即生效,那麼就會阻礙不少優化方法,但這樣對程序員是有好處的,由於程序員很容易推斷程序的執行結果,這樣寫出的程序就容易與本身的意圖一致。這種內存模型被稱爲順序一致性模型(Sequential Consistency)。反之,若是爲了優化程序執行效率,重排序的可能性有不少,那麼程序的效率是提升了,但對程序員來講,就很難推斷程序的執行結果。這一類的內存模型被稱爲Relaxed Memory Model。java

這樣,咱們就遇到了一個兩難的問題:git

  • 內存模型要求嚴格,那麼程序效率低,但程序員容易寫對
  • 內存模型要求鬆,那麼程序效率高,但程序員不容易寫對

而程序的效率,與程序是否容易寫對都很重要。爲了解決這個問題,科學家提出了 Data Race Free 的概念,它是對多線程程序同步程度的一種描述,基本的思想是若是多線程程序進行了正確的同步,那麼程序員就能夠按照順序一致性模型去推斷程序的執行結果,而底層對內存操做的實現,能夠按照 Relaxed Memory Model進行優化。程序員

Java 內存模型包含了兩方面的內容github

  • 對正確同步的多線程程序,保證其執行結果與在順序內存模型下執行的結果一致
  • 對沒有正確同步要求的多線程程序,進行必定程度的限制,以保證安全性

其中第一方面是與 Data Race Free相關的,第二方面與後面介紹的 Causality Requirements 相關。安全

Data Race Free

Java 內存模型其實定義了好幾個概念來講明什麼是正確的同步。markdown

  • 衝突訪問(conflicting accesses)

若是存在多個線程,同時訪問同一地址,而且至少有一個是寫操做,那麼這個程序存在衝突訪問多線程

  • happen-before order

兩個操做之間若是知足下面任意一個條件,就能夠說這兩個操做之間存在 happen-before order:併發

  1. 同一個線程內,在程序中有前後次序的操做
  2. 構造器的結尾的操做與 finalize 函數的開始的操做
  3. unlock 操做與全部同一把鎖上的 lock操做
  4. volatile 變量的讀操做與全部對它的寫操做
  5. 對變量默認值的寫操做與線程啓動後的第一個操做
  6. 若是線程 T2 檢測到線程 T1 終止執行,那麼 T1 的最後一次操做與 T2任意操做
  7. 啓動一個線程的操做與此線程內第一個操做
  8. 若是線程 T1 中斷了線程 T2,那麼此中斷操做與其它任何看到 T2 被中斷的操做之間。

其中有些我也不是很理解。。oracle

  • data race free

全部存在衝突訪問的操做之間都有 happen-before order,那麼此多線程程序知足 data race free

  • 正確同步

假如多線程程序在順序一致性模型下執行,若是它知足 data race free,那麼此程序進行了正確的同步。

正確同步的多線程程序,其執行結果與在順序一致性模型下的執行結果一致。仔細體會下概念之間的關係。有點繞。

另外一方面,若是程序沒有正確同步,執行結果也不是任意的,必須對其進行限制,但限制又不能太強,由於太強會阻礙優化。因此 Java 內存模型使用了 Causality Requirements 的概念。

Causality Requirements

爲了精肯定義內存模型,Java語言規範中,提出了 Causality Requirements 的概念。不知道是什麼緣由,這個概念不多被說起,可是我以爲它是很重要的,但同時,也是很是使人費解的。語言規範中,首先定義了 Well-Formed Executions 的概念,如今對內存模型的不少討論,都是在這一層,它包括了對多線程程序執行中,與鎖,volatile變量,執行次序等等相關的規定。若是一個多線程程序的執行知足這些規定,那麼這個執行就是 Well-Formed Executions 的。國內有一個系列文章《深刻理解Java內存模型》,主要是在這方面描述Java內存模型。此外,在 Java 併發領域內著名的 Doug Lea 也給出了一個 The JSR-133 Cookbook for Compiler Writers,爲編譯器做者們提供參考,探討的也是這方面的問題。可是,內存模型對多線程程序的執行是否合法,不只僅要看它是不是 Well-Formed Executions,此次執行還須要知足 Causality Requirements。

語言規範中規定了一個構造過程,若是經過這個構造過程,能夠構造出多線程程序最終的執行結果,那麼此次執行就知足 Causality Requirements。構造過程從一個空集合C0開始,每次將其中添加若干操做,若是全部操做都能被添加,那麼構形成功。即,

C0 -> C1 -> C2 -> ... -> C

其中 C_i 是 C_(i+1) 的子集。你可能注意到了,以前說的「操做能被添加」,什麼叫操做能被添加呢?語言規範中規定了,每個 Ci 都對應一個 Ei,全部 Ei 都要知足 Well-Formed Executions。也就是說,若是你添加了操做後,對應的 Ei 不知足 Well-Formed Executions,那麼這個操做就不能被添加。若是最終,你的多線程程序沒法構造出這樣一個執行鏈,那麼,它的執行結果是非法的。

另外,Java 內存模型最初論文做者維護了一個頁面 The Java Memory Model,其中有一個條目叫 Causality Test Cases,給出了一些小例子,以便人們明白哪些行爲是知足 Causality Requirements 的,哪些是不知足的。此外,在 Java 併發領域內著名的 Doug Lea 也給出了一個 The JSR-133 Cookbook for Compiler Writers,爲編譯器做者們提供參考。不過聽說這份規範有些地方要求太嚴格了,開發者們仍是根據Java語言規範和虛擬機規範來開發。


參考資料:Java 語言規範以內存模型

相關文章
相關標籤/搜索