Java的內存模型

首先給出定義,Java內存模型(Java Memory Model ,JMM)是一種符合內存模型規範的,屏蔽了各類硬件和操做系統的訪問差別的,保證了Java程序在各類平臺下對內存的訪問都能保證效果一致的機制及規範。程序員

在弄懂JMM以前,咱們要先了解下CPU和內存是如何交互的。編程

CPU和高速緩存以及內存(主存)的交互

image.png從圖中能夠看出在多CPU的系統中,每一個CPU都有都有各自的高速緩存,通常分爲L1L2L3緩存,由於這些緩存的存在,提供了數據的訪問性能,也減輕了數據總線上數據傳輸的壓力,而主內存卻只有一個 。CPU要讀取一個數據時,首先從一級緩存中查找,若是沒有找到再從二級緩存中查找,若是仍是沒有就從三級緩存或內存中查找,每一個CPU有且只有一套本身的緩存。緩存

可是問題也就來了,若是兩個CPU同時去操做同一個內存地址,會發生什麼?也就是說,如何保證多個處理器運算涉及到同一個內存區域時,多線程場景下的緩存一致性問題?運行時如何保證數據一致性?那就是內存屏障(Memory Barrier)。安全

內存屏障多線程

CPU中的高速緩存提升了數據訪問性能,避免每次都向內存索取,可是不能實時的和內存發生信息交換。在不一樣CPU執行的不一樣線程對同一個變量的緩存值多是不一樣的,由此就出現了內存屏障,硬件層的內存屏障分爲兩種:Load BarrierStore Barrier即讀屏障和寫屏障。架構

內存屏障的做用主要有兩點:併發

  • 阻止屏障兩側指令重排序
  • 強制把寫緩衝區/高速緩存中的髒數據等寫回主內存,讓緩存中相應的數據失效。

之因此扯了那麼多計算機內存模型,是由於Java內存模型的設定符合了計算機的規範。函數

實際上,JMMJVM的一種規範,定義了JVM的內存模型。它屏蔽了各類硬件和操做系統的訪問差別,不像C那樣直接訪問硬件內存,相對安全不少。它的主要目的是解決因爲多線程經過共享內存進行通訊時,存在的本地內存數據不一致、編譯器會對代碼指令重排序、處理器會對代碼亂序執行等帶來的問題。能夠保證併發編程場景中的原子性、可見性和有序性。性能

Java內存模型的應用

Java中的幾個關鍵字:volatilefinalsynchronized,能夠幫助程序員把代碼中的併發需求描述給編譯器。Java內存模型中定義了它們的行爲,以確保正確同步的Java代碼在全部的處理器架構上都能正確執行。操作系統

volatile

Java中,volatile關鍵字能夠解決上面的問題,Java屏蔽掉這些差別,經過JVM生成內存屏障的指令。

當咱們聲明某個變量爲volatile修飾時,這個變量就有了線程可見性,volatile會在讀寫操做先後添加內存屏障。volatile字段的每次讀行爲都能看到其它線程最後一次對該字段的寫行爲,經過它就能夠避免拿到緩存中陳舊數據。它們必須保證在被寫入以後,會被刷新到主內存中,這樣就能夠當即對其它線程能夠見。

final

若是一個類包含final字段,且在構造函數中初始化,那麼正確的構造一個對象後,final字段被設置後對於其它線程是可見的。

注意這裏所說的正確構造對象,意思是在對象的構造過程當中,不容許對該對象進行引用,否則的話,可能存在其它線程在對象還沒構造完成時就對該對象進行訪問,形成其餘的問題。

synchronized

對於一個被synchronized修飾的monitor對象,只可以被一個線程持有,意味着一旦有線程進入了同步代碼塊,那麼其它線程就不能進入,直到第一個進入的線程退出代碼塊。

在一個線程退出同步塊時,線程釋放monitor對象,它的做用是把CPU緩存數據(本地緩存數據)刷新到主內存中,從而實現該線程的行爲能夠被其它線程看到。在其它線程進入到該代碼塊時,須要得到monitor對象,它在做用是使CPU緩存失效,從而使變量從主內存中從新加載,而後就能夠看到以前線程對該變量的修改。

但從緩存的角度看,這個問題只會影響多處理器的機器,對於單核來講沒什麼問題,可是它還有一個語義是禁止指令的重排序,對於編譯器來講,同步塊中的代碼不會移動到獲取和釋放monitor的外面。

總結

JMMJVM的一種規範,定義了JVM的內存模型。它的主要目的是解決因爲多線程經過共享內存進行通訊時,存在的本地內存數據不一致、編譯器會對代碼指令重排序、處理器會對代碼亂序執行等帶來的問題。能夠保證併發編程場景中的原子性、可見性和有序性。

Java中,volatilefinalsynchronized這三個關鍵字是對與內存模型的具體實現。

本文由博客一文多發平臺 OpenWrite 發佈!

更多內容請點擊個人博客 沐晨

相關文章
相關標籤/搜索