天天看點

并發程式設計學習(17)-----Java記憶體模型引言:一.平台的記憶體模型二.Happens-Before關系

思維導圖:

并發程式設計學習(17)-----Java記憶體模型引言:一.平台的記憶體模型二.Happens-Before關系

引言:

    在前面的文章中,我們刻意的避免了對java記憶體模型JMM的介紹。實際上,正是java記憶體模型決定了對java代碼的重排序。重排序實際就是多個線程對變量改動的不可見的原因,因為在代碼邏輯上A線程成的操作在B線程的操作之前進行,但是進過JVM進行重排序後,可能B線程的操作就會在A線程之前進行。通過了解java記憶體模型,我們可以知道,滿足什麼樣的條件,JVM就不會進行重排序。

    本章我們會通過原理部分對java記憶體模型進行介紹。

  • 原理部分:先介紹JVM的java記憶體模型,然後介紹Happens-Before關系,JVM由這個關系決定是否重排序。

一.平台的記憶體模型

    在共享記憶體的多處理器體系架構中,每個處理器都擁有自己的緩存,并且定期的與主記憶體進行協調。是以,當保持最小的緩存一緻性時,系統會允許不同的處理器在同一時刻從同一儲存位置看到的值時不同的。如果想要確定在任意時間各個處理器在同一位置看到的值相同則需要相當大的開銷,而這在大多數時間是不需要的。

    我們的程式會執行一種簡單假設,想象在程式中隻存在唯一的操作順序,而不考慮這些操作在何種處理器上執行,并且在每次讀取變量時,都能會得在執行序列中最近一次寫入該變量的值。這個樂觀的模型被稱為串行一緻性。

    在實際操作中,如果多個線程通路同一位置時,很有可能出現資料不同步的問題。因為在編譯期生成指令順序時是可以與源代碼中的順序不同。此外,編譯期也可能會把變量儲存在寄存器中而不是記憶體中,處理器也可能采用亂序或并行等方式來執行指令,緩存也可能會改變将寫入變量送出到主存的次序,而且,儲存在處理器本地緩存的值在其他處理器是不可見的。這些都會導緻多個線程通路同一個資料但是結果卻不同步的問題。是以,我們在編寫并發程式時需要借助鎖或者非阻塞同步機制來進行控制。

二.Happens-Before關系

    java記憶體模型為所有的操作定義了一個偏序關系,稱之為Happens-Before。如果想要保證執行操作B的線程看到操作A的結果,那麼在A和B之間必須滿足Happens-Before關系,無論他們是否是在同一個線程之中。

Happens-Before關系如下所示:

  1. 程式順序規則:如果程式中操作A在操作B之前,那麼線上程中A操作将在B操作之前執行。
  2. 螢幕鎖規則:在螢幕鎖上的解鎖操作必須在同一個螢幕鎖的加鎖操作之前執行。
  3. volatile變量規則:對volatile變量的寫入操作必須在對該變量的讀取操作之前執行。
  4. 線程啟動規則:線上程上對Thread.start的調用不許在該線程中執行任何操作之前執行。
  5. 線程結束規則:線程中的任何操作都必須在其他線程檢測到該線程結束之前執行,或者從Thread.join中成功傳回,或者在調用Thread.isAlive時傳回false
  6. 中斷規則:當一個線程在另一個線程中調用intterupt時,必須在中斷線程檢測到interrupt調用之前執行。
  7. 終結器規則:對象的構造器函數必須在啟動該對象的終結器之前執行完成。
  8. 傳遞性規則:如果操作A在操作B之前執行,并且操作B在操作C之前執行,那麼操作A必須在操作C之前執行。

    以上就是Happens-Before規則,當我們需要最大限度的提升某些類的性能時,我們可以利用此規則實作對某個未被鎖保護的變量的通路操作進行排序。但是這種操作對執行順序非常敏感,是以特别容易出錯。

繼續閱讀