Java并發(fā)內(nèi)存模型詳情
Java
是一門支持多線程執(zhí)行的語言,要編寫正確的并發(fā)程序,了解Java內(nèi)存模型是重要前提。而了解硬件內(nèi)存模型有助于理解程序的執(zhí)行。
本文主要整理以下內(nèi)容
- Java內(nèi)存模型
- 硬件內(nèi)存架構(gòu)
- 共享對象可見性
- 競爭條件
1、Java內(nèi)存模型
Java內(nèi)存模型最新修訂是在Java5
。 JSR-176
羅列了 J2SE5.0
相關(guān)發(fā)布特性,包含其中的 JSR-133
(JavaTM內(nèi)存模型與線程規(guī)范),java虛擬機遵循此規(guī)范。延續(xù)至今該內(nèi)存模型在Java8中依然奏效。
JSR 全稱
Java Specification Requests
,意為Java標(biāo)準(zhǔn)化技術(shù)規(guī)范的正式請求。
Java程序運行在虛擬機上(Jvm)。從邏輯角度看,Jvm內(nèi)存被劃分為線程堆棧和堆。每個線程都擁有自己的堆棧,該線程堆棧存儲的數(shù)據(jù)不對其它線程可見。堆內(nèi)存用于存儲共享數(shù)據(jù)。
線程堆棧存儲方法中所有局部變量,包含原始類型(boolean
,byte
,short
,char
,int
,long
, float
,double
)和對象引用。
堆存儲需要共享對象和靜態(tài)變量。
注意:對象不一定都會存儲到堆內(nèi)存??聪旅胬?,假如果Object對象不需要被其它線程共享,編譯器會執(zhí)行堆分配轉(zhuǎn)化為棧分配。
解釋一下,編譯器會根據(jù)對象是否逃逸做出優(yōu)化。優(yōu)化的其中一項就是堆分配轉(zhuǎn)化為棧分配,目的在于減輕GC壓力,提升性能。此優(yōu)化動作由Jvm參數(shù)-XX:+DoEscapeAnalysi 進行控制。Java8 默認(rèn)開啟。
測試:通過開啟或關(guān)閉 -XX:+PrintGC -XX:-DoEscapeAnalysis
觀察是否執(zhí)行GC來判斷對象存儲位置。
public static void main(String[] args){ for(int i = 0; i < 10000000; i++){ createObj(); } } public static void createObj(){ new Object(); }
2、硬件內(nèi)存架構(gòu)
如下圖,現(xiàn)代計算機通常都裝有2個或者更多的CPU
,CPU
又可以是多核。一個CPU
包含一組寄存器,每個CPU
具有一個高速緩存,而高速緩存又分為L1,L2,L3,L4 不同層級緩存。
RAM
為主存儲也就是我們說的計算機內(nèi)存,所有CPU都可以讀取主存儲。
當(dāng)CPU
讀取主存儲數(shù)據(jù)時,它會將部分主存儲數(shù)據(jù)讀入CPU高速緩存中,又將緩存的中一部分讀入寄存器執(zhí)行,操作結(jié)束后,將值從寄存器刷新到高速緩存中,高速緩存在特定的時刻將數(shù)據(jù)統(tǒng)一刷新到內(nèi)存中。
3、實際執(zhí)行
事實上,上面闡述的Java
堆棧內(nèi)存模型是為了理解抽象出來的。實際執(zhí)行就像下圖一樣,線程棧和堆的數(shù)據(jù)可能分散到硬件不同的存儲區(qū)域。數(shù)據(jù)分散在不同區(qū)域會帶來以下兩個主要問題。
3.1 共享對象可見性
下面場景兩個線程同時操作對象obj.count
,其中一個線程對obj.count
進行更新,但是對其它線程不可見。
線程A操作obj時,先從主存里拷貝一個數(shù)據(jù)副本到CPU高速緩存,又到寄存器,然后修改obj.count=2
后刷新到CPU高速緩存,但是數(shù)據(jù)暫未同步到主存。以此同時線程B也操作obj
,拷貝的數(shù)據(jù)副本仍然為obj.count=1
,這會導(dǎo)致程序結(jié)果錯誤。
解決此問題,可以使用Java volatile
關(guān)鍵字。volatile
可簡單理解為跳過CPU高速緩存,讓修改結(jié)果及時同步到主存,從而保證了其它線程讀到最新值。volatile
后期專門介紹。
3.2 競爭條件
另外一種情況假如果多個線程同時更行obj.count
,這時會發(fā)生競爭條件。
解決方法,使用Java synchronized
保證線程執(zhí)行順序,另外synchronized
包裹中的所有變量都直接從主存讀?。ㄌ^CPU高速緩存),并且當(dāng)線程退出synchronized
后,所有更新的變量將同步到主存。
總結(jié):
本文記錄Java內(nèi)存模型,其中主要內(nèi)容來源于 Jakob Jenkov 大神博客。
http://tutorials.jenkov.com/java-concurrency/java-memory-model.html
到此這篇關(guān)于Java并發(fā)內(nèi)存模型詳情的文章就介紹到這了,更多相關(guān)Java并發(fā)內(nèi)存模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Nacos?動態(tài)服務(wù)發(fā)現(xiàn)、配置和服務(wù)管理平臺初體驗
這篇文章主要介紹了Nacos?動態(tài)服務(wù)發(fā)現(xiàn)、配置和服務(wù)管理平臺初體驗的相關(guān)資料,需要的朋友可以參考下2022-09-09SpringBoot數(shù)據(jù)庫恢復(fù)的兩種方法mysqldump和mysqlbinlog
binlog用來實現(xiàn)主從復(fù)制,也常用來誤刪數(shù)據(jù)庫找回丟失的記錄,本文主要介紹了SpringBoot數(shù)據(jù)庫恢復(fù)的兩種方法mysqldump和mysqlbinlog,具有一定的參考價值,感興趣的可以了解一下2024-01-01使用SpringBoot項目導(dǎo)入openfeign版本的問題
這篇文章主要介紹了使用SpringBoot項目導(dǎo)入openfeign版本的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03電腦上安裝多個JDK版本時該如何自由切換(詳細(xì)圖文)
我們在學(xué)習(xí)的過程中經(jīng)常用到不同的jdk版本,那么如何在一臺電腦上同時安裝多個jdk版本并進行切換呢,這篇文章主要給大家介紹了關(guān)于電腦上安裝多個JDK版本時該如何自由切換的相關(guān)資料,需要的朋友可以參考下2023-10-10