您好,登錄后才能下訂單哦!
本篇內容介紹了“如何掌握Java并發內存模型”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
1、Java內存模型
2、硬件內存架構
3、實際執行
3.1 共享對象可見性
3.2 競爭條件
Java
是一門支持多線程執行的語言,要編寫正確的并發程序,了解Java內存模型是重要前提。而了解硬件內存模型有助于理解程序的執行。
本文主要整理以下內容
Java內存模型
硬件內存架構
共享對象可見性
競爭條件
Java內存模型最新修訂是在Java5
。 JSR-176
羅列了 J2SE5.0
相關發布特性,包含其中的 JSR-133
(JavaTM內存模型與線程規范),java虛擬機遵循此規范。延續至今該內存模型在Java8中依然奏效。
JSR 全稱
Java Specification Requests
,意為Java標準化技術規范的正式請求。
Java程序運行在虛擬機上(Jvm)。從邏輯角度看,Jvm內存被劃分為線程堆棧和堆。每個線程都擁有自己的堆棧,該線程堆棧存儲的數據不對其它線程可見。堆內存用于存儲共享數據。
線程堆棧存儲方法中所有局部變量,包含原始類型(boolean
,byte
,short
,char
,int
,long
, float
,double
)和對象引用。
堆存儲需要共享對象和靜態變量。
注意:對象不一定都會存儲到堆內存。看下面例子,假如果Object對象不需要被其它線程共享,編譯器會執行堆分配轉化為棧分配。
解釋一下,編譯器會根據對象是否逃逸做出優化。優化的其中一項就是堆分配轉化為棧分配,目的在于減輕GC壓力,提升性能。此優化動作由Jvm參數-XX:+DoEscapeAnalysi 進行控制。Java8 默認開啟。
測試:通過開啟或關閉 -XX:+PrintGC -XX:-DoEscapeAnalysis
觀察是否執行GC來判斷對象存儲位置。
public static void main(String[] args){ for(int i = 0; i < 10000000; i++){ createObj(); } } public static void createObj(){ new Object(); }
如下圖,現代計算機通常都裝有2個或者更多的CPU
,CPU
又可以是多核。一個CPU
包含一組寄存器,每個CPU
具有一個高速緩存,而高速緩存又分為L1,L2,L3,L4 不同層級緩存。
RAM
為主存儲也就是我們說的計算機內存,所有CPU都可以讀取主存儲。
當CPU
讀取主存儲數據時,它會將部分主存儲數據讀入CPU高速緩存中,又將緩存的中一部分讀入寄存器執行,操作結束后,將值從寄存器刷新到高速緩存中,高速緩存在特定的時刻將數據統一刷新到內存中。
事實上,上面闡述的Java
堆棧內存模型是為了理解抽象出來的。實際執行就像下圖一樣,線程棧和堆的數據可能分散到硬件不同的存儲區域。數據分散在不同區域會帶來以下兩個主要問題。
下面場景兩個線程同時操作對象obj.count
,其中一個線程對obj.count
進行更新,但是對其它線程不可見。
線程A操作obj時,先從主存里拷貝一個數據副本到CPU高速緩存,又到寄存器,然后修改obj.count=2
后刷新到CPU高速緩存,但是數據暫未同步到主存。以此同時線程B也操作obj
,拷貝的數據副本仍然為obj.count=1
,這會導致程序結果錯誤。
解決此問題,可以使用Java volatile
關鍵字。volatile
可簡單理解為跳過CPU高速緩存,讓修改結果及時同步到主存,從而保證了其它線程讀到最新值。volatile
后期專門介紹。
另外一種情況假如果多個線程同時更行obj.count
,這時會發生競爭條件。
解決方法,使用Java synchronized
保證線程執行順序,另外synchronized
包裹中的所有變量都直接從主存讀取(跳過CPU高速緩存),并且當線程退出synchronized
后,所有更新的變量將同步到主存。
“如何掌握Java并發內存模型”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。