Java併發以內存模型

Java是一門支持多線程執行的語言,要編寫正確的併發程序,瞭解Java內存模型是重要前提。而瞭解硬件內存模型有助於理解程序的執行。html

本文主要整理如下內容java

  • Java內存模型
  • 硬件內存架構
  • 共享對象可見性
  • 競爭條件

Java內存模型

Java內存模型最新修訂是在Java5。 JSR-176 羅列了 J2SE5.0 相關發佈特性,包含其中的 JSR-133(JavaTM內存模型與線程規範),java虛擬機遵循此規範。延續至今該內存模型在Java8中依然奏效。緩存

JSR 全稱 Java Specification Requests,意爲Java標準化技術規範的正式請求。

Java程序運行在虛擬機上(Jvm)。從邏輯角度看,Jvm內存被劃分爲線程堆棧。每一個線程都擁有本身的堆棧,該線程堆棧存儲的數據不對其它線程可見。堆內存用於存儲共享數據。多線程

堆棧模型

線程堆棧存儲方法中全部局部變量,包含原始類型(boolean,byte,short,char,int,long, float,double)和對象引用。架構

堆存儲須要共享對象和靜態變量。併發

注意 對象不必定都會存儲到堆內存。看下面例子,假若是Object對象不須要被其它線程共享,編譯器會執行堆分配轉化爲棧分配性能

解釋一下,編譯器會根據 對象是否逃逸作出優化。優化的其中一項就是 堆分配轉化爲棧分配,目的在於減輕GC壓力,提高性能。此優化動做由Jvm參數-XX:+DoEscapeAnalysi 進行控制。Java8 默認開啓。

測試:經過開啓或關閉 -XX:+PrintGC -XX:-DoEscapeAnalysis 觀察是否執行GC來判斷對象存儲位置。學習

public static void main(String[] args){
        for(int i = 0; i < 10000000; i++){
            createObj();
        }
    }
    public static void createObj(){
        new Object();
    }

堆棧模型

硬件內存架構

以下圖,現代計算機一般都裝有2個或者更多的CPU,CPU又能夠是多核。一個CPU包含一組寄存器,每一個CPU具備一個高速緩存,而高速緩存又分爲L1,L2,L3,L4 不一樣層級緩存。測試

RAM爲主存儲也就是咱們說的計算機內存,全部CPU均可以讀取主存儲。優化

當CPU讀取主存儲數據時,它會將部分主存儲數據讀入CPU高速緩存中,又將緩存的中一部分讀入寄存器執行,操做結束後,將值從寄存器刷新到高速緩存中,高速緩存在特定的時刻將數據統一刷新到內存中。

硬件內存架構

實際執行

事實上,上面闡述的Java堆棧內存模型是爲了理解抽象出來的。實際執行就像下圖同樣,線程棧和堆的數據可能分散到硬件不一樣的存儲區域。數據分散在不一樣區域會帶來如下兩個主要問題。

堆棧模型與硬件內存架構關聯

共享對象可見性

下面場景兩個線程同時操做對象obj.count,其中一個線程對obj.count進行更新,可是對其它線程不可見。

線程A操做obj時,先從主存裏拷貝一個數據副本到CPU高速緩存,又到寄存器,而後修改obj.count=2後刷新到CPU高速緩存,可是數據暫未同步到主存。以此同時線程B也操做obj,拷貝的數據副本仍然爲obj.count=1,這會致使程序結果錯誤。

解決此問題,可使用Java volatile關鍵字。volatile可簡單理解爲跳過CPU高速緩存,讓修改結果及時同步到主存,從而保證了其它線程讀到最新值。volatile 後期專門介紹。

共享對象可見性

競爭條件

另一種狀況假若是多個線程同時更行obj.count,這時會發生競爭條件

解決方法,使用Java synchronized 保證線程執行順序,另外synchronized包裹中的全部變量都直接從主存讀取(跳過CPU高速緩存),而且當線程退出synchronized後,全部更新的變量將同步到主存。

競爭條件

總結

本文記錄Java內存模型,其中主要內容來源於 Jakob Jenkov 大神博客。

歡迎你們留言交流,一塊兒學習分享!!!

http://tutorials.jenkov.com/j...
相關文章
相關標籤/搜索