一文搞懂: Java內存模式

前言

Java內存模式: 是一種虛擬機規范,規定了一個線程如何和何時可以看到由其他線程修改過后的共享變量的值,以及在必須時如何同步的訪問共享變量。

根據jvm內存模型我們知道,調用棧和本地變量存放在線程棧上,對象存放在堆上
如下圖(JVM內存模型)我們可以知道

  • 調用棧和本地變量是線程“私有”
  • 堆是線程“共有”
    JVM內存模型

那么到底什么是線程公有,什么是線程私有(可參考:一文搞懂: JVM內存模型以及GC回收機制

一:硬件內存架構

現代硬件內存模型與Java內存模型有一些不同,理解內存模型架構以及Java內存模型如何與它協同工作也是非常重要的。

現代計算機硬件架構的簡單圖示:

  • CPU寄存器:每個CPU都包含一系列的寄存器,它們是CPU內內存的基礎。CPU在寄存器上執行操作的速度遠大于在主存上執行的速度。這是因為CPU訪問寄存器的速度遠大于主存。
  • 高速緩存cache:由于計算機的存儲設備與處理器的運算速度之間有著幾個數量級的差距,所以現代計算機系統都不得不加入一層讀寫速度盡可能接近處理器運算速度的高速緩存(Cache)來作為內存與處理器之間的緩沖:將運算需要使用到的數據復制到緩存中,讓運算能快速進行,當運算結束后再從緩存同步回內存之中,這樣處理器就無須等待緩慢的內存讀寫了。CPU訪問緩存層的速度快于訪問主存的速度,但通常比訪問內部寄存器的速度還要慢一點。每個CPU可能有一個CPU緩存層,一些CPU還有多層緩存。在某一時刻,一個或者多個緩存行(cache lines)可能被讀到緩存,一個或者多個緩存行可能再被刷新回主存。
  • 內存:一個計算機還包含一個主存。所有的CPU都可以訪問主存。主存通常比CPU中的緩存大得多。
  • 運作原理:通常情況下,當一個CPU需要讀取主存時,它會將主存的部分讀到CPU緩存中。它甚至可能將緩存中的部分內容讀到它的內部寄存器中,然后在寄存器中執行操作。當CPU需要將結果寫回到主存中去時,它會將內部寄存器的值刷新到緩存中,然后在某個時間點將值刷新回主存。

以上說了這么多,總結來說就是:在一個線程當中,如果我想改變一個對象的值的時候(這個對象在堆內存中,也就是我們所說的主內存),它會把這個值讀取到高速緩存中(也可以理解為copy了這個值到緩存中),然后把這個值讀取到CPU內部寄存器中進行相對應的改變,然后再某一時刻,把改變之后的值放到高速緩存中再刷新到主內存中

二:多線程中的主內存對象修改

多線程中的主內存對象修改,就涉及到了多個線程同時修改一個主內存對象,那么線程間通信必須要經過主內存。如下,如果線程A與線程B之間要通信的話,必須要經歷下面2個步驟:
1) 線程A把本地內存A中更新過的共享變量刷新到主內存中去
2) 線程B到主內存中去讀取線程A之前已更新過的共享變量

線程通訊圖解

三:Java內存模型解決的問題

3.1 可見性

我們來看以下這種多線程場景:
跑在左邊CPU的線程拷貝這個共享對象到它的CPU緩存中,然后將count變量的值修改為2。這個修改對跑在右邊CPU上的其它線程是不可見的,因為修改后的count的值還沒有被刷新回主存中去


場景一

為了達到多個線程看到同一個值達到我們預期的效果,我們需要滿足線程的“可見性”

可見性(共享對象可見性):線程對共享變量修改的可見性。當一個線程修改了共享變量的值,其他線程能夠立刻得知這個修改

線程緩存導致的可見性問題:
如果兩個或者更多的線程在沒有正確的使用volatile聲明或者同步的情況下共享一個對象,一個線程更新這個共享對象可能對其它線程來說是不可見的:共享對象被初始化在主存中。跑在CPU上的一個線程將這個共享對象讀到CPU緩存中,然后修改了這個對象。只要CPU緩存沒有被刷新會主存,對象修改后的版本對跑在其它CPU上的線程都是不可見的。這種方式可能導致每個線程擁有這個共享對象的私有拷貝,每個拷貝停留在不同的CPU緩存中。

解決這個內存可見性問題你可以使用:

Java中的volatile關鍵字:volatile關鍵字可以保證直接從主存中讀取一個變量,如果這個變量被修改后,總是會被寫回到主存中去。Java內存模型是通過在變量修改后將新值同步回主內存,在變量讀取前從主內存刷新變量值這種依賴主內存作為傳遞媒介的方式來實現可見性的,無論是普通變量還是volatile變量都是如此,普通變量與volatile變量的區別是:volatile的特殊規則保證了新值能立即同步到主內存,以及每個線程在每次使用volatile變量前都立即從主內存刷新。因此我們可以說volatile保證了多線程操作時變量的可見性,而普通變量則不能保證這一點。

Java中的synchronized關鍵字:同步快的可見性是由“如果對一個變量執行lock操作,將會清空工作內存中此變量的值,在執行引擎使用這個變量前需要重新執行load或assign操作初始化變量的值”、“對一個變量執行unlock操作之前,必須先把此變量同步回主內存中(執行store和write操作)”這兩條規則獲得的。

3.2 原子性

我們再來看以下這種多線程場景:
果線程A讀一個共享對象的變量count到它的CPU緩存中,線程B也做了同樣的事情,但是往一個不同的CPU緩存中。現在線程A將count加1,線程B也做了同樣的事情。現在count已經被增加了兩次,每個CPU緩存中一次。如果這些增加操作被順序的執行,變量count應該被增加兩次,然后原值+2被寫回到主存中去。然而,兩次增加都是在沒有適當的同步下并發執行的。無論是線程A還是線程B將count修改后的版本寫回到主存中取,修改后的值僅會被原值大1(就是如果做了同步,此時應該為3),盡管增加了兩次:

場景二

很明顯,此時有個臨界條件,就是兩個線程同時把主存中的數據讀取到CPU緩存中,那么為了解決這個問題,我們就要滿足多線程的原子性
原子性:持有同一個鎖的兩個同步塊只能串行地進入

如果應用場景需要一個更大范圍的原子性保證,需要使用同步塊技術。Java內存模型提供了lock和unlock操作來滿足這種需求。虛擬機提供了字節碼指令monitorenter和monitorexist來隱式地使用這兩個操作,這兩個字節碼指令反映到Java代碼中就是同步快——synchronized關鍵字。

總結來說:volatile保證了可見性、synchronized保證了原子性和可見性

END

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,695評論 2 370

推薦閱讀更多精彩內容