專題1-3-java內存模型JMM

1 概念介紹

        JMM是java爲了解決多線程通過對共享內存進行通信時存在的本地內存數據不一致問題(可見性問題)、編譯器會對代碼指令重排序(有序性問題)、處理器會對代碼亂序執行(原子性問題)等問題的一種規範。java虛擬機(JVM)由硬件發展而來,JMM也是由計算內存模型發展而來的。

2 計算機內存模型

2.1 CPU直接讀取階段

       最初階段,CPU的處理速度和內存的讀寫速度相匹配,所以CPU直接從內存中讀取數據沒有問題。

2.2 CPU讀取單個緩存

       CPU處理技術在不斷地發展,內存的技術並沒有多大變化,從內存中讀取數據的速度遠遠跟不上CPU的處理速度,因此在CPU和內存之間引入了高速緩存。它的特點是速度快,內存小並且昂貴。

2.3 CPU讀取多級緩存

       隨着技術的發展,緩存變成了多級緩存,CPU也得到了發展,由單核變成了多核。單核CPU只含有一套L1,L2,L3緩存;如果CPU含有多個核心,即多核CPU,則每個核心都含有一套L1(甚至和L2)緩存,而共享L3(或者和L2)緩存。當CPU要讀取一個數據時,首先從一級緩存中查找,如果沒有找到再從二級緩存中查找,如果還是沒有就從三級緩存或內存中查找。

2.4 存在問題

(1)緩存一致性問題

       單線程。cpu核心的緩存只被一個線程訪問。緩存獨佔,不會出現訪問衝突等問題。

       單核CPU,多線程。進程中的多個線程會同時訪問進程中的共享數據,CPU將某塊內存加載到緩存後,不同線程在訪問相同的物理地址的時候,都會映射到相同的緩存位置,這樣即使發生線程的切換,緩存仍然不會失效。但由於任何時刻只能有一個線程在執行,因此不會出現緩存訪問衝突。

        多核CPU,多線程。每個核都至少有一個L1 緩存。多個線程訪問進程中的某個共享內存,且這多個線程分別在不同的核心上執行,則每個核心都會在各自的caehe中保留一份共享內存的緩衝。由於多核是可以並行的,可能會出現多個線程同時寫各自的緩存的情況,而各自的cache之間的數據就有可能不同。

       在CPU和主存之間增加緩存,在多線程場景下就可能存在緩存一致性問題,也就是說,在多核CPU中,每個核的自己的緩存中,關於同一個數據的緩存內容可能不一致。

(2)處理器優化和指令重排

       爲了使處理器內部的運算單元能夠儘量的被充分利用,處理器可能會對輸入代碼進行亂序執行處理。這就是處理器優化。Java虛擬機的即時編譯器(JIT)也會做指令重排。我們的代碼順序被打亂,指令被重排,就可能不會按照我們的意願去執行了。

       內存模型解決併發問題主要採用兩種方式:限制處理器優化和使用內存屏障。

3  java內存模型JMM

       java內存模型在計算機內存模型下發展而來,它和計算機內存模型極爲相似。所以也存在緩存一致性問題和指令重排問題,其中指令重排的過程如下圖所示

JVM爲了解決這些問題提出來JMM模型,結構如下:

 

(1)模型規定了所有的變量都存儲在主內存中,每條線程還有自己的工作內存,線程的工作內存中保存了該線程中用到的變量的主內存副本拷貝。

(2)線程對變量的所有操作都必須在工作內存中進行,而不能直接讀寫主內存。

(3)不同的線程之間也無法直接訪問對方工作內存中的變量,線程間變量的傳遞均需要自己的工作內存和主內存之間進行數據同步進行。

      通過這個模型解決了多線程下多核CPU的緩存一致性問題(可見性)。Java中的volatile關鍵字提供了一個功能,那就是被其修飾的變量在被修改後可以立即同步到主內存,被其修飾的變量在每次使用之前都從主內存刷新。因此,可以使用volatile來保證多線程操作時變量的可見性。除volatile,Java中的synchronized和final兩個關鍵字也可以實現可見性。

      由於編譯器指令重排帶來的問題(原子性,有序性),Java中可以使synchronized和和volatile來保證方法和代碼塊內的操作是有序性。synchronized來保證操作的原子性。volatile關鍵字會禁止指令重排。synchronized關鍵字保證同一時刻只允許一條線程操作。

博客地址:

https://blog.csdn.net/renchunlin66

碼雲社區地址:

https://gitee.com/renchunlin66

公衆號請搜索:“快樂的一隻”

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章