直接起飛。
什么是JMM內存模型?
這並不是一個實際存在的東西,並不是一個實際存在的東西,並不是一個實際存在的東西;重要的事情說3遍,只是一種抽象概念。個人理解,JMM內存模型描述的是java線程和硬件通信的一個過程。
標准解釋:
Java內存模型(Java Memory Model簡稱JMM)是一種抽象的概念,並不真實存在,它 描述的是一組規則或規范,通過這組規范定義了程序中各個變量(包括實例字段,靜態字段 和構成數組對象的元素)的訪問方式。JVM運行程序的實體是線程,而每個線程創建時 JVM都會為其創建一個工作內存(有些地方稱為棧空間),用於存儲線程私有的數據,而Java 內存模型中規定所有變量都存儲在主內存,主內存是共享內存區域,所有線程都可以訪問, 但線程對變量的操作(讀取賦值等)必須在工作內存中進行,首先要將變量從主內存拷貝的自 己的工作內存空間,然后對變量進行操作,操作完成后再將變量寫回主內存,不能直接操作 主內存中的變量,工作內存中存儲着主內存中的變量副本拷貝,前面說過,工作內存是每個 線程的私有數據區域,因此不同的線程間無法訪問對方的工作內存,線程間的通信(傳值)必 須通過主內存來完成。
圖解JMM內存模型
主內存:
主要存儲的是Java實例對象,所有線程創建的實例對象都存放在主內存中,不管該實例 對象是成員變量還是方法中的本地變量(也稱局部變量),當然也包括了共享的類信息、常 量、靜態變量。由於是共享數據區域,多條線程對同一個變量進行訪問可能會發生線程安全 問題。(不要理解成是JVM的堆內存或者原空間,這是兩個不同概念)
工作內存:
主要存儲當前方法的所有本地變量信息(工作內存中存儲着主內存中的變量副本拷貝), 每個線程只能訪問自己的工作內存,即線程中的本地變量對其它線程是不可見的,就算是兩 個線程執行的是同一段代碼,它們也會各自在自己的工作內存中創建屬於當前線程的本地變 量,當然也包括了字節碼行號指示器、相關Native方法的信息。注意由於工作內存是每個線程的私有數據,線程間無法相互訪問工作內存,因此存儲在工作內存的數據不存在線程安全問題。(不要理解成棧幀,JMM內存模型和JVM內存模型是兩碼事)
JMM內存模型和JVM內存模型的區別?
JMM與JVM內存區域的划分是不同的概念層次,更恰當說JMM描述的是一組規則,通 過這組規則控制程序中各個變量在共享數據區域和私有數據區域的訪問方式,JMM是圍繞 原子性,有序性,可見性展開。JMM與Java內存區域唯一相似點,都存在共享數據區域和 私有數據區域,在JMM中主內存屬於共享數據區域,從某個程度上講應該包括了堆和方法 區,而工作內存數據線程私有數據區域,從某個程度上講則應該包括程序計數器、虛擬機棧 以及本地方法棧。
根據JVM虛擬機規范主內存與工作內存的數據存儲類型以及操作方式,對於一個實例對 象中的成員方法而言,如果方法中包含本地變量是基本數據類型 (boolean,byte,short,char,int,long,float,double),將直接存儲在工作內存的幀棧結構 中,但倘若本地變量是引用類型,那么該變量的引用會存儲在功能內存的幀棧中,而對象實 例將存儲在主內存(共享數據區域,堆)中。但對於實例對象的成員變量,不管它是基本數據 類型或者包裝類型(Integer、Double等)還是引用類型,都會被存儲到堆區。至於static變 量以及類本身相關信息將會存儲在主內存中。需要注意的是,在主內存中的實例對象可以被 多線程共享,倘若兩個線程同時調用了同一個對象的同一個方法,那么兩條線程會將要操作 的數據拷貝一份到自己的工作內存中,執行完成操作后才刷新到主內存。
如果硬要說JMM內存模型和JVM內存模型關系,那就是下圖所示:
方法棧幀上的臨界資源會到主內存中。
JMM內存模型和硬件內存架構關系?
通過上一篇博客硬件的認知、Java內存模型以及Java多線程的實現原理的了解,我們應該已經意識到,多線程的執行最終都會映射到硬件處理器上進行執行,但Java內存模型和硬 件內存架構並不完全一致。對於硬件內存來說只有寄存器、緩存內存、主內存的概念,並沒 有工作內存(線程私有數據區域)和主內存(堆內存)之分,也就是說Java內存模型對內存的划分對硬件內存並沒有任何影響,因為JMM只是一種抽象的概念,是一組規則,並不實際存在,不管是工作內存的數據還是主內存的數據,對於計算機硬件來說都會存儲在計算機主內存中,當然也有可能存儲到CPU緩存或者寄存器中,因此總體上來說,Java內存模型和計算機硬件內存架構是一個相互交叉的關系,是一種抽象概念划分與真實物理硬件的交叉。(注意對於Java內存區域划分也是同樣的道理)
為什么要存在JMM這抽象概念?
由於JVM運行程序的實體是線 程,而每個線程創建時JVM都會為其創建一個工作內存(有些地方稱為棧空間),用於存儲線 程私有的數據,線程與主內存中的變量操作必須通過工作內存間接完成,主要過程是將變量 從主內存拷貝的每個線程各自的工作內存空間,然后對變量進行操作,操作完成后再將變量 寫回主內存,如果存在兩個線程同時對一個主內存中的實例對象的變量進行操作就有可能誘 發線程安全問題。
JMM模型可以很清晰的描述多線程帶來問題,並且與程序和硬件有很好的契合解釋。
數據同步八大原子操作
(1)lock(鎖定):作用於主內存的變量,把一個變量標記為一條線程獨占狀態
(2)unlock(解鎖):作用於主內存的變量,把一個處於鎖定狀態的變量釋放出來,釋放后 的變量才可以被其他線程鎖定
(3)read(讀取):作用於主內存的變量,把一個變量值從主內存傳輸到線程的工作內存 中,以便隨后的load動作使用
(4)load(載入):作用於工作內存的變量,它把read操作從主內存中得到的變量值放入工 作內存的變量副本中
(5)use(使用):作用於工作內存的變量,把工作內存中的一個變量值傳遞給執行引擎
(6)assign(賦值):作用於工作內存的變量,它把一個從執行引擎接收到的值賦給工作內 存的變量
(7)store(存儲):作用於工作內存的變量,把工作內存中的一個變量的值傳送到主內存 中,以便隨后的write的操作
(8)write(寫入):作用於工作內存的變量,它把store操作從工作內存中的一個變量的值 傳送到主內存的變量中
如果要把一個變量從主內存中復制到工作內存中,就需要按順序地執行read和load操 作,如果把變量從工作內存中同步到主內存中,就需要按順序地執行store和write操作。但 Java內存模型只要求上述操作必須按順序執行,而沒有保證必須是連續執行。
共享變量(臨界資源)的同步分析:
1)不允許一個線程無原因地(沒有發生過任何assign操作)把數據從工作內存同步回主內 存中
2)一個新的變量只能在主內存中誕生,不允許在工作內存中直接使用一個未被初始化 (load或者assign)的變量。即就是對一個變量實施use和store操作之前,必須先自行 assign和load操作。
3)一個變量在同一時刻只允許一條線程對其進行lock操作,但lock操作可以被同一線程重 復執行多次,多次執行lock后,只有執行相同次數的unlock操作,變量才會被解鎖。lock和unlock必須成對現。
4)如果對一個變量執行lock操作,將會清空工作內存中此變量的值,在執行引擎使用這個 變量之前需要重新執行load或assign操作初始化變量的值。
5)如果一個變量事先沒有被lock操作鎖定,則不允許對它執行unlock操作;也不允許去 unlock一個被其他線程鎖定的變量。
6)對一個變量執行unlock操作之前,必須先把此變量同步到主內存中(執行store和write 操作)
學習一時爽,一直學習一直爽。