摘要:內(nèi)存模型即,簡稱,其規(guī)范了虛擬機與計算機內(nèi)存時如何協(xié)同工作的,規(guī)定了一個線程如何和何時看到其他線程修改過的值,以及在必須時,如何同步訪問共享變量。內(nèi)存模型要求調(diào)用棧和本地變量存放在線程棧上,對象存放在堆上。
Java內(nèi)存模型即Java Memory Model,簡稱JMM,其規(guī)范了Java虛擬機與計算機內(nèi)存時如何協(xié)同工作的,規(guī)定了一個線程如何和何時看到其他線程修改過的值,以及在必須時,如何同步訪問共享變量。
JVM的內(nèi)存分配在解釋Java內(nèi)存模型之前,我們先了解下JVM的內(nèi)存分配的幾個概念,如下圖所示,Java內(nèi)存模型把內(nèi)存分為兩大塊,一個是堆一個是棧。
堆heap:運行時的數(shù)據(jù)區(qū),由垃圾回收負(fù)責(zé),動態(tài)分配大小。存取速度較慢;
棧stack:存取速度比堆快,僅次于寄存器,數(shù)據(jù)可以共享,大小和生存期等是固定的。
Java內(nèi)存模型要求調(diào)用棧和本地變量存放在線程棧上,對象存放在堆上。
一個變量也可能是指向一個對象的引用,引用這個變量是放在線程棧上,但對象本身是放在堆上的。
一個對象它可能包含方法(methodOne..),方法包含本地變量(Local variable1..),這些本地變量都仍然是放在線程棧上的,即使這些方法所屬的對象存放在堆上,一個對象的成員變量可能會隨著對象自身存放在堆上,不管這個對象是原始類型還是引用類型。
靜態(tài)成員變量跟隨著類的定義存放在堆上,存放在堆上的對象可以被所持有對這個對象引用的線程訪問。
當(dāng)一個線程可以訪問一個對象時,它也可以訪問這個對象的成員變量,如果兩個線程同時調(diào)用同一個對象上的同一個方法,他們都會訪問這個對象的成員變量,但是每一個線程都擁有了成員變量的私有拷貝。
接下來我們再來看看計算機硬件架構(gòu)的圖示:
這里是個多CPU的結(jié)構(gòu),一個cpu中可能還包含多核。因此我們可以看出,在有兩個或者多個cpu的現(xiàn)代計算機上,同時運行多個線程是有可能的,而且每個cpu在某個時刻運行一個線程是沒問題的。若Java程序是多線程的,在Java程序中,每個cpu上一個線程是可能同時并發(fā)執(zhí)行的。
在CPU內(nèi)部有一組CPU寄存器,也就是CPU的儲存器。
CPU操作寄存器的速度要比操作計算機主存快的多,在主存和CPU寄存器之間還存在一個CPU緩存,CPU操作CPU緩存的速度快于主存但慢于CPU寄存器。即CPU操作的速度上主存 < 緩存 < 寄存器。某些CPU可能有多個緩存層(一級緩存和二級緩存)。計算機的主存也稱作RAM,所有的CPU都能夠訪問主存,而且主存比上面提到的緩存和寄存器大很多。
當(dāng)一個CPU需要訪問主存時,會先讀取一部分主存數(shù)據(jù)到CPU緩存,進而在讀取CPU緩存到寄存器。當(dāng)CPU需要寫數(shù)據(jù)到主存時,同樣會先將寄存器的數(shù)據(jù)刷新到CPU緩存,然后再在某些節(jié)點把緩存數(shù)據(jù)刷新到主存。
Java內(nèi)存模型和硬件內(nèi)存架構(gòu)并不一致。硬件內(nèi)存架構(gòu)中并沒有區(qū)分棧和堆,從硬件上看,所有的線程棧和堆都分布在主主存中,當(dāng)然一部分棧和堆的數(shù)據(jù)也有可能會存到CPU緩存和寄存器中,如下圖所示,Java內(nèi)存模型和計算機硬件內(nèi)存架構(gòu)是一個交叉關(guān)系:
并發(fā)編程模型的兩個關(guān)鍵問題在并發(fā)編程中,需要處理兩個關(guān)鍵問題:線程之間如何通信及線程之間如何同步(這里的線程是指并發(fā)執(zhí)行的活動實體)。通信是指線程之間以何種機制來交換信息。在命令式編程中,線程之間的通信機制有兩種:共享內(nèi)存和消息傳遞。
在共享內(nèi)存的并發(fā)模型里,線程之間共享程序的公共狀態(tài),通過寫-讀內(nèi)存中的公共狀態(tài)進行隱式通信。在消息傳遞的并發(fā)模型里,線程之間沒有公共狀態(tài),線程之間必須通過發(fā)送消息來顯式進行通信。
同步是指程序中用于控制不同線程間操作發(fā)生相對順序的機制。在共享內(nèi)存并發(fā)模型里,同步是顯式進行的。程序員必須顯式指定某個方法或某段代碼需要在線程之間互斥執(zhí)行。
在消息傳遞的并發(fā)模型里,由于消息的發(fā)送必須在消息的接收之前,因此同步是隱式進行的。
Java的并發(fā)采用的是共享內(nèi)存模型,Java線程之間的通信總是隱式進行,整個通信過程對程序員完全透明。
接下來我們從抽象角度看看線程和主存之間的抽象關(guān)系:
線程之間的共享變量存儲在主內(nèi)存里,每個線程都有個私有的本地內(nèi)存,存儲了該線程以讀/寫共享變量的副本。它涵蓋了緩存、寫緩沖區(qū)、寄存器以及其他的硬件和編譯器優(yōu)化。本地內(nèi)存是JMM的一個抽象概念,并不真實存在。
從上圖來看,如果線程A與線程B之間要通信的話,必須要經(jīng)歷下面2個步驟。
1)線程A把本地內(nèi)存A中更新過的共享變量刷新到主內(nèi)存中去。
2)線程B到主內(nèi)存中去讀取線程A之前已更新過的共享變量。
本地內(nèi)存A和本地內(nèi)存B由主內(nèi)存中共享變量x的副本。假設(shè)初始時,這3個內(nèi)存中的x值都為0。線程A在執(zhí)行時,把更新后的x值(假設(shè)值為1)臨時存放在自己的本地內(nèi)存A中。當(dāng)線程A和線程B需要通信時,線程A首先會把自己本地內(nèi)存中修改后的x值刷新到主內(nèi)
存中,此時主內(nèi)存中的x值變?yōu)榱?。隨后,線程B到主內(nèi)存中去讀取線程A更新后的x值,此時線程B的本地內(nèi)存的x值也變?yōu)榱?。
從整體來看,這兩個步驟實質(zhì)上是線程A在向線程B發(fā)送消息,而且這個通信過程必須要經(jīng)過主內(nèi)存。JMM通過控制主內(nèi)存與每個線程的本地內(nèi)存之間的交互,來為Java程序員提供內(nèi)存可見性保證。
為了保證并發(fā)時程序處理的準(zhǔn)確性,這里就需要一些同步的手段,這里我們介紹一下Java內(nèi)存模型定義的同步的八種操作和一些規(guī)則。
八種操作lock(鎖定):作用于主內(nèi)存的變量,把一個變量標(biāo)識為一條線程獨占狀態(tài);
unlock(解鎖):作用于主內(nèi)存的變量,把一個處于鎖定狀態(tài)的變量釋放出來,釋放后的變量才可以被其他線程鎖定;
read(讀?。鹤饔糜谥鲀?nèi)存的變量,把一個變量值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中,以便隨后的load動作使用;
load(載入):作用于工作內(nèi)存的變量,它把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中;
use(使用):作用于工作內(nèi)存的變量,把工作內(nèi)存中的一個變量值傳遞給執(zhí)行引擎;
assign(賦值): 作用于工作內(nèi)存的變量,它把一個執(zhí)行引擎接受到的值賦值給工作內(nèi)存的變量;
store(存儲):作用于工作內(nèi)存的變量,把工作內(nèi)存中的一個變量的值傳送到主內(nèi)存中,以便隨后的write操作;
write(寫入): 作用于主內(nèi)存的變量,他把store操作從工作內(nèi)存中一個變量的值傳送到主內(nèi)存的變量中。
規(guī)則不允許read/load,store/write單一出現(xiàn),且必須按順序執(zhí)行,但中間可以插入其他指令;
不允許一個線程丟棄離他最近的assign操作
不允許一個線程未發(fā)生assign操作就將數(shù)據(jù)同步至主線程;
一個新的變量,只能從主內(nèi)存中誕生,不允許在工作內(nèi)存中生成一個未被初始化的變量。
一個變量在同一時刻只允許一個線程執(zhí)行l(wèi)ock操作,lock可以被同一個線程執(zhí)行多次,需要相同次數(shù)的unlock操作才能解鎖;
如果一個變量執(zhí)行了lock操作,則會清空工作內(nèi)存中的值,執(zhí)行引擎使用這個變量前需要重新執(zhí)行l(wèi)oad或者assign操作來拿到初始化變量的值;
如果一個變量沒有被lock操作執(zhí)行,則不允許對其進行unlock操作,也不允許unlock一個被其他線程lock的變量,unlock操作執(zhí)行之前,必須將此變量同步回主內(nèi)存。
參考文檔:
《Java并發(fā)編程的藝術(shù)》
https://blog.csdn.net/suifeng...
https://coding.imooc.com/lear...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69274.html
摘要:本文是個人在企業(yè)內(nèi)部分享使用的簡要大綱,列舉了的重要更新,文章的結(jié)構(gòu)較簡單,也不規(guī)范,鑒于近期寫若干文章時總會忘記一些新特性所處的版本,特將此大綱流留用。 本文是個人在企業(yè)內(nèi)部分享使用的簡要大綱,列舉了JAVA9-12的重要更新,文章的結(jié)構(gòu)較簡單,也不規(guī)范,鑒于近期寫若干文章時總會忘記一些新特性所處的版本,特將此大綱流copy留用。 一 JAVA9 新特性 1.Java Platfo...
摘要:以下為大家整理了阿里巴巴史上最全的面試題,涉及大量面試知識點和相關(guān)試題。的內(nèi)存結(jié)構(gòu),和比例。多線程多線程的幾種實現(xiàn)方式,什么是線程安全。點擊這里有一套答案版的多線程試題。線上系統(tǒng)突然變得異常緩慢,你如何查找問題。 以下為大家整理了阿里巴巴史上最全的 Java 面試題,涉及大量 Java 面試知識點和相關(guān)試題。 JAVA基礎(chǔ) JAVA中的幾種基本數(shù)據(jù)類型是什么,各自占用多少字節(jié)。 S...
閱讀 920·2021-11-22 13:53
閱讀 2544·2021-10-15 09:40
閱讀 1015·2021-10-14 09:42
閱讀 3615·2021-09-22 15:59
閱讀 905·2021-09-02 09:47
閱讀 2418·2019-08-30 15:54
閱讀 1454·2019-08-29 17:14
閱讀 414·2019-08-29 15:15