閱讀338 返回首頁    go 技術社區[雲棲]


JAVA 內存模型(一)

綜述:

 簡單的說,Java的內存模型定義了在一個線程對一個共享變量進行修改後,修改後的共享變量什麼時候對其它線程可見。

作用:

  1. 對於程序員 JMM給程序員呈現出來的是具有順序一致性的強內存模型。(通俗點就是所見即所得)
  2. 對於處理器與編譯器 JMM給處理器與編譯器提供了一個比較弱的happens-before內存模型,這樣盡可能多的給處理器與編譯器優化代碼的空間。

處理器與編譯器的優化技術

  1. 編譯器優化的重排序。 編譯器在不改變單線程程序語義的前提下,可以對我們編寫的代碼進行重排。如下:
public class Test { 
    public static void main(String args []) {
        int a = 1;       // 1
        int b = 2;      // 2
        // 在單線程語義下這個程序的輸出為 1
        // 交換標注為 1,2 的兩行代碼不改變單線程程序語義
        // 編譯器可以對這樣的代碼進行指令重排,以提高程序運行的性能
        System.out.println(a);
    }       
 }  
public class Test {
    public static void main(String args[ ]) {
        int a = 0;                          // 1
        a = 2;                              // 2
        a = 3;                              // 3
        // 在單線程語義下這個程序的輸出為3
        // 如果標注為 2 ,3的代碼進行交換順序,那就改變了單線程語義,JMM
        // 是禁止這種重排序的。
        System.out.println(a);  
    }
}

2 . 處理器的指令級並行重排序
現代處理器采用了指令級並行技術,來對多條指令進行重排序。如果指令間不存在數據依賴(對同一個變量寫後讀,讀後寫,寫後寫),那處理器就可以改變指令間的執行順序。如下:

  0x00007f31c9108ac0: mov    %eax,-0x14000(%rsp)
  0x00007f31c9108ac7: push   %rbp
  0x00007f31c9108ac8: sub    $0x30,%rsp
  0x00007f31c9108acc: movabs $0x7f31c8c00448,%rdi  ;   {metadata(method data for {method} {0x00007f31c8c00260} 'test' '()V' in 'testTwo')}
  0x00007f31c9108ad6: mov    0xdc(%rdi),%ebx
  // 假設以上5條匯編指令之間不存在數據依賴,如果處理器的流水線大於等於5,那麼,這5條匯編指令可以並行的進行計算。

3 . 內存係統的重排序
由於處理器使用緩存,這使得加載和存儲操作看上去是在亂序執行。如圖[1]:
screenshot
intel i7 處理器是4核8線程,每個核心都有自己的L1, L2 緩存,當不同的核心緩存相同的共享變量,並寫回到L3 緩存時,就存在內存係統的重排序。

Happens-before 規則

  綜述:happens-before 關係是由Lamport(1978)這位大神提出的。它的表述為: a -> b 讀作“a 在 b 之前發生”,意思是所有的進程(分布式係統中)/ 線(我們現在討論的JMM)一致認為事件 a  先於事件 b 發生:*注意:這裏的先發生實際上是一種可見性的表述,它並不代表在物理時間上事件a先於事件b發生,而是事件b在執行前要能看到事件a執行後的結果。*這就為處理器,與編譯器的重排提供了保證。

我們來看看JSR-133對JMM中happens-before規則的示例:
1. 程序順序規則:一個線程中的的每個操作,happens-before於該線程中的任意後續操作。
2. 監視器規則: 對一個鎖的解鎖,happens-before於隨後對這個鎖的加鎖。
3. volatile變量規則:對一個volatile域的寫,happens-before與任意後續對這個volatile域的讀。
4. 傳遞性: 如果 a -> b, b -> c, 則 a -> c.
5. 線程啟動 start() 規則:如果線程A 執行操作ThreadB.start()(啟動線程B),那麼A線程的啟動線程的操作,happens-before於線程B中的任意操作。
6. join()規則: 如果線程A 執行ThreadB.join()並成功返回,那麼線程B中的任意操作,happens-before 於線程A 從ThreadB.join()操作成功返回。

順序一致性內存模型

綜述:順序一致性模型是一個被計算機科學家理想化了的理論參考模型,它為程序員提供了內存可見性的保證。
順序一致性的兩大特征:
1. 一個線程中所有操作必須按照程序的順序來執行
2.不管程序是否同步,所有線程都隻能看到一個單一的操作執行順序。在順序一致性內存模型中,每個操作都必須原子執行且對所有線程立即可見。

本博客是對《JAVA並發編程的藝術》方騰飛,魏鵬,程曉明 著。第三章的讀後感。

參考資料:[1] intel開發技術手冊卷3第2章

最後更新:2017-06-25 19:02:19

  上一篇:go  阿裏雲上Docker企業版之旅
  下一篇:go  從入門型到企業型:雲服務器開放共享型到獨享型規格升級變配