可以看一下我的另一篇總結 JVM運行時數據區與JVM堆內存模型小結
推薦一篇文章,尚學堂的 Java內存模型深度解讀 。
不方便全文轉載,就摘錄下吧。
以往的認知都是以基本類型、引用類型、常量、方法等來區分堆棧以及方法區常量池 -- 嗯,個人認知。
但這篇文章卻刷新了認知:
1、JVM內存分為堆、線程棧。每個JVM線程都有自己的線程棧,其中存的是線程相關的信息(調用方法就會有線程?)。--主要是這點,其他的仍然相通。但不確定是否只有堆棧。
2、線程棧存儲的都是變量的私有副本,線程棧之間只能有私有副本的值拷貝傳遞,而不能有私有副本的共享!(務必理解此時引用類型的值拷貝是指拷貝引用,而非具體的對象)
3、靜態成員變量跟隨着類定義一起也存放在堆上!
計算機的硬件架構(簡化示意):
通常情況下,當一個CPU需要讀取主存時,它會將主存的部分讀到CPU緩存中。它甚至可能將緩存中的部分內容讀到它的內部寄存器中,然后在寄存器中執行操作。當CPU需要將結果寫回到主存中去時,它會將其內部寄存器的值刷新到緩存中,然后在某個時間點將值刷新回主存。-- 關鍵,不是實時刷新。
Java內存模型與硬件內存架構之間存在差異。硬件內存架構沒有區分線程棧和堆。對於硬件,所有的線程棧和堆都分布在主內中。部分線程棧和堆可能有時候會出現在CPU緩存中和CPU內部的寄存器中。如下:
可能導致的問題:
緩存之間不能直接交換數據,必須通過主存。而緩存與主存之間並非實時同步。所以,多線程時,當線程棧建立在緩存上,而堆建立在主存上時,彼此之間的數據是不同步的。
1、當多個線程共享一個對象時,一個線程進行修改操作,對另一個線程來說可能是不可見的。
2、當多個線程共享一個對象時,兩個線程都進行修改操作,競爭關系。
解決辦法:
針對不可見情況
,volatile
關鍵字可以保證直接從主存中讀取一個變量,如果這個變量被修改后,總是會被寫回到主存中去。
針對競爭情況,一個同步塊可以保證在同一時刻僅有一個線程可以進入代碼的臨界區(操作系統概念?)。同步塊還可以保證代碼塊中所有被訪問的變量將會從主存中讀入,當線程退出同步代碼塊時,所有被更新的變量都會被刷新回主存中去,不管這個變量是否被聲明為 volatile 。
其他概念:
臨界區內的數據一次只能同時被一個進程使用,當一個進程使用臨界區內的數據時,其他需要使用臨界區數據的進程進入等待狀態。