Java 内存模型(JMM)
文章目錄
- 什么是JMM模型
- JMM和JVM的區(qū)別
- 主內(nèi)存和工作內(nèi)存
- Java內(nèi)存模型與硬件內(nèi)存架構(gòu)的關(guān)系
- JMM存在的必要性
- JMM數(shù)據(jù)同步八大原子操作
- CPU緩存一致性協(xié)議MESI
- 為什么要有高速緩存?
- 帶有高速緩存的CPU執(zhí)行計(jì)算的流程
- 目前流行的多級(jí)緩存結(jié)構(gòu)
- CPU底層全執(zhí)行流程
- 同步規(guī)則分析
- 并發(fā)編程的可見(jiàn)性、原子性和有序性問(wèn)題
- JMM是如何解決原子性&可見(jiàn)性&有序性問(wèn)題
- 指令重排序
- as-if-serial 語(yǔ)義
- happens-before原則
什么是JMM模型
Java內(nèi)存模型(Java Memory Model簡(jiǎn)稱(chēng)JMM)是一種抽象的概念,并不真實(shí)存在,它描述的是一組規(guī)則或規(guī)范,通過(guò)這組規(guī)范定義了程序中各個(gè)變量(包括實(shí)例字段,靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素)的訪(fǎng)問(wèn)方式。
JVM運(yùn)行程序的實(shí)體是線(xiàn)程,而每個(gè)線(xiàn)程創(chuàng)建時(shí),JVM都會(huì)為其創(chuàng)建一個(gè)工作內(nèi)存(有些地方稱(chēng)為棧空間),用于存儲(chǔ)線(xiàn)程私有的數(shù)據(jù)。
Java內(nèi)存模型中規(guī)定所有變量都存儲(chǔ)在主內(nèi)存,主內(nèi)存是共享內(nèi)存區(qū)域,所有線(xiàn)程都可以訪(fǎng)問(wèn),但線(xiàn)程對(duì)變量的操作(讀取賦值等)必須在工作內(nèi)存中進(jìn)行,首先要將變量從主內(nèi)存拷貝的自己的工作內(nèi)存空間,然后對(duì)變量進(jìn)行操作,操作完成后再將變量寫(xiě)回主內(nèi)存,不能直接操作主內(nèi)存中的變量,工作內(nèi)存中存儲(chǔ)著主內(nèi)存中的變量副本拷貝。
JMM和JVM的區(qū)別
- JMM是圍繞原子性,有序性、可見(jiàn)性展開(kāi)。
- JMM與Java內(nèi)存區(qū)域唯一相似點(diǎn),都存在共享數(shù)據(jù)區(qū)域和私有數(shù)據(jù)區(qū)域,在JMM中主內(nèi)存屬于共享數(shù)據(jù)區(qū)域,從某個(gè)程度上講應(yīng)該包括了堆和方法區(qū),而工作內(nèi)存數(shù)據(jù)線(xiàn)程私有數(shù)據(jù)區(qū)域,從某個(gè)程度上講則應(yīng)該包括程序計(jì)數(shù)器、虛擬機(jī)棧以及本地方法棧。
主內(nèi)存和工作內(nèi)存
主要存儲(chǔ)的是Java實(shí)例對(duì)象,所有線(xiàn)程創(chuàng)建的 實(shí)例對(duì)象都存放在主存中,不管該實(shí)例對(duì)象時(shí)成員邊浪還是方法中的本地變量,當(dāng)然還包含共享的類(lèi)信息、常量、靜態(tài) 變量。由于是共享數(shù)據(jù)區(qū)域,多條線(xiàn)程對(duì)同一個(gè)變量進(jìn)行訪(fǎng)問(wèn)可能會(huì)發(fā)生線(xiàn)程安全問(wèn)題。
- 主要存儲(chǔ)當(dāng)前方法的所有本地變量(工作內(nèi)存中存儲(chǔ)著主內(nèi)存中的變量副本拷貝),每個(gè)線(xiàn)程只能訪(fǎng)問(wèn)自己的工作內(nèi)存,即線(xiàn)程中的本地變量對(duì)其他線(xiàn)程是不可見(jiàn)的,就算是兩個(gè)線(xiàn)程執(zhí)行的是同一段代碼,它們也會(huì)各自在自己的工作內(nèi)存中創(chuàng)建屬于當(dāng)前線(xiàn)程的本地變量,當(dāng)然也包括了字節(jié)碼行號(hào)指示器、相關(guān)Native方法的信息。
- 由于工作內(nèi)存是每個(gè)線(xiàn)程的私有數(shù)據(jù),線(xiàn)程間無(wú)法相互訪(fǎng)問(wèn)工作內(nèi)存,因此存儲(chǔ)在工作內(nèi)存的數(shù)據(jù)不存在線(xiàn)程安全問(wèn)題。
- 根據(jù)JVM 虛擬機(jī)規(guī)范主內(nèi)存與工作內(nèi)存的數(shù)據(jù)存儲(chǔ)類(lèi)型以及操作方式,對(duì)于一個(gè)實(shí)例對(duì)象中的成員方法而言,如果方法中包含本地變量是基本類(lèi)型,將直接存儲(chǔ)在工作內(nèi)存的棧幀結(jié)構(gòu)中,但倘若本地變量是引用類(lèi)型,那么該變量的引用會(huì)存儲(chǔ)在工作內(nèi)存的棧幀中,而對(duì)象實(shí)例將存儲(chǔ)在主內(nèi)存(共享內(nèi)存區(qū)域,堆中)。
- 但對(duì)于實(shí)例對(duì)象的成員變量,不管它是基本數(shù)據(jù)類(lèi)型或者包裝類(lèi)型還是引用類(lèi)型,都會(huì)存儲(chǔ)在堆中。
- 至于static變量以及類(lèi)本身相關(guān)信息將會(huì)存儲(chǔ)在主內(nèi)存中。
- 在主內(nèi)存中的實(shí)例對(duì)象可以被多線(xiàn)程共享,倘若兩個(gè)線(xiàn)程同時(shí)調(diào)用了同一對(duì)象的同一方法,那么兩條線(xiàn)程會(huì)將要操作的數(shù)據(jù)拷貝一份到自己的工作內(nèi)存中,執(zhí)行完成操作后才刷新到主內(nèi)存中。
Java內(nèi)存模型與硬件內(nèi)存架構(gòu)的關(guān)系
對(duì)于硬件內(nèi)存來(lái)說(shuō)只有寄存器、緩存內(nèi)存、主內(nèi)存的概念,并沒(méi)有工作內(nèi)存之分,也就是說(shuō)Java內(nèi)存模型對(duì)內(nèi)存的劃分對(duì)硬件內(nèi)存并沒(méi)有任何影響,因?yàn)镴MM只是一種抽象的概念,是一組規(guī)則,并不實(shí)際存在,不管是工作內(nèi)存的數(shù)據(jù)還是主 內(nèi)存的數(shù)據(jù),對(duì)于計(jì)算機(jī)硬件來(lái)說(shuō)都會(huì)存儲(chǔ)在計(jì)算機(jī)主內(nèi)存中,當(dāng)然也有可能存儲(chǔ)在到CPU緩存或者寄存器中
因此總體上來(lái) 說(shuō),Java內(nèi)存模型和計(jì)算機(jī)硬件內(nèi)存架構(gòu)是一個(gè)相互交叉的關(guān)系,是一種抽象概念劃分與真實(shí)物理硬件的交互。
JMM存在的必要性
由于JVM運(yùn)行程序的實(shí)體是線(xiàn)程,而每個(gè)線(xiàn)程創(chuàng)建時(shí)JVM都會(huì)為其創(chuàng)建一個(gè)工作內(nèi)存(有些地方稱(chēng)為棧空間),用于存儲(chǔ)線(xiàn)程私有的數(shù)據(jù),線(xiàn)程與主內(nèi)存中的變量操作必須通過(guò)工作內(nèi)存間接完成,主要過(guò)程是將變量從主內(nèi)存拷貝的每個(gè)線(xiàn)程各自的工作內(nèi)存空間,然后對(duì)變量進(jìn)行操作,操作完成后再將變量寫(xiě)回主內(nèi)存,如果存在兩個(gè)線(xiàn)程同時(shí)對(duì)一個(gè)主內(nèi)存中的實(shí)例對(duì)象的變量進(jìn)行操作就有可能誘發(fā)線(xiàn)程安全問(wèn)題。
假設(shè)主內(nèi)存中存在一個(gè)共享變量X,現(xiàn)在有A和B兩條線(xiàn)程分別對(duì)該變量x=1進(jìn)行操作,A和B線(xiàn)程各自在自己的工作內(nèi)存中存在共享變量副本X.
假設(shè)現(xiàn)在A線(xiàn)程想要修改x的值 為2,而B(niǎo)線(xiàn)程卻想要讀取X的值,那么B線(xiàn)程讀取到的值是A線(xiàn)程更新后的值2還是更新前的值1呢?答案是不確定,即B線(xiàn)程 有可能讀取到A線(xiàn)程更新前的值1,也有可能讀取到A 線(xiàn)程更新后的值2,這是因?yàn)楣ぷ鲀?nèi)存是每個(gè)線(xiàn)程私有的數(shù)據(jù)區(qū)域,而線(xiàn)程A變量x時(shí),首先是講變量從主內(nèi)存拷貝到A線(xiàn)程的工作內(nèi)存中,然后對(duì)變量進(jìn)行操作,操作完成 后再將變量X寫(xiě)回主內(nèi)存中,而對(duì)于B線(xiàn)程也是類(lèi)似,這樣就有可能造成主內(nèi)存與工作內(nèi)存間數(shù)據(jù)存在不一致性問(wèn)題。
假設(shè)A線(xiàn)程修改完成后正在將數(shù)據(jù)寫(xiě)回主內(nèi)存,而B(niǎo)線(xiàn)程此時(shí)正在讀取主內(nèi)存,即將x=1拷貝到自己的工作呢次云中,這樣B線(xiàn)程讀取的值就是x=1,但如果A線(xiàn)程已將x=2寫(xiě)回主內(nèi)存后,B線(xiàn)程才開(kāi)始讀取的話(huà),那么此時(shí)B線(xiàn)程讀取的就是x=2,但到底是哪種情況先發(fā)生呢?
JMM數(shù)據(jù)同步八大原子操作
(1)lock(鎖定):作用于主內(nèi)存的變量,把一個(gè)變量標(biāo)記為一條線(xiàn)程獨(dú)占狀態(tài)
(2)unlock(解鎖):作用于主內(nèi)存的變量,把一個(gè)處于鎖定狀態(tài)的變量釋放出來(lái),釋放后的變量才可以被其他線(xiàn)程鎖定。
(3)read(讀取):作用于主內(nèi)存的變量,把一個(gè)變量值從主內(nèi)存?zhèn)鬏數(shù)骄€(xiàn)程的工作內(nèi)存中,以便隨后的load動(dòng)作使用。
(4)load(載入):作用于工作內(nèi)存的變量,它把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中。
(5)use(使用):作用于工作內(nèi)存的變量,把工作內(nèi)存中的一個(gè)變量值傳遞給執(zhí)行引擎。
(6)assign(賦值):作用于工作內(nèi)存的變量,它把一個(gè)從執(zhí)行引擎接收到的值賦給工作內(nèi)存的變量。
(7)store(存儲(chǔ)):作用于工作內(nèi)存的變量,把工作內(nèi)存中的一個(gè)變量的值傳送到主內(nèi)存中,以便隨后的write的操作。
(8)write(寫(xiě)入):作用于工作內(nèi)存的變量,它把store操作從工作內(nèi)存中的一個(gè)變量的值傳送到主內(nèi)存的變量中。
CPU緩存一致性協(xié)議MESI
為什么要有高速緩存?
-
CPU在摩爾定律的指導(dǎo)下以每18個(gè)月翻一番的速度在發(fā)展,然而內(nèi)存和硬盤(pán)的發(fā)展速度遠(yuǎn)遠(yuǎn)不及CPU。這就造成了高性能能的內(nèi)存和硬盤(pán)價(jià)格及其昂貴。然而CPU的高度運(yùn)算需要高速的數(shù)據(jù)。為了解決這個(gè)問(wèn)題,CPU廠(chǎng)商在CPU中內(nèi)置了少量的高速緩存以解決I\O速度和CPU運(yùn)算速度之間的不匹配問(wèn)題。在CPU訪(fǎng)問(wèn)存儲(chǔ)設(shè)備時(shí),無(wú)論是存取數(shù)據(jù)抑或存取指令,都趨于聚集在一片連續(xù)的區(qū)域中,這就被稱(chēng)為局部性原理。
-
時(shí)間局部 性
如果一個(gè)信息項(xiàng)正在被訪(fǎng)問(wèn),那么在近期它很可能還會(huì)被再次訪(fǎng)問(wèn)。比如循環(huán)、遞歸、方法的反復(fù)調(diào)用等。
-
空間局部性
如果一個(gè)存儲(chǔ)器的位置被引用,那么將來(lái)他附近的位置也會(huì)被引用。比如順序執(zhí)行的代碼、連續(xù)創(chuàng)建的兩個(gè)對(duì)象、數(shù)組等。
帶有高速緩存的CPU執(zhí)行計(jì)算的流程
1.程序以及數(shù)據(jù)被加載到主內(nèi)存
2.指令和數(shù)據(jù)被加載到CPU的高速緩存
3.CPU執(zhí)行指令,把結(jié)果寫(xiě)到高速緩存
4.高速緩存中的數(shù)據(jù)寫(xiě)回主內(nèi)存
目前流行的多級(jí)緩存結(jié)構(gòu)
由于CPU的運(yùn)算速度超越了1級(jí)緩存的數(shù)據(jù)I\O能力,CPU廠(chǎng)商又引入了多級(jí)的緩存結(jié)構(gòu)。
CPU底層全執(zhí)行流程
同步規(guī)則分析
1)不允許一個(gè)線(xiàn)程無(wú)原因地(沒(méi)有發(fā)生過(guò)任何assign操作)把數(shù)據(jù)從工作內(nèi)存同步回主內(nèi)存中。
2)一個(gè)新的變量只能在主內(nèi)存中誕生,不允許在工作內(nèi)存中直接使用一個(gè)未被初始化(load或者assign)的變量。即就是對(duì)一個(gè)變量實(shí)施use和store操作之前,必須先自行assign和load操作。
3)一個(gè)變量在同一時(shí)刻只允許一條線(xiàn)程對(duì)其進(jìn)行l(wèi)ock操作,但lock操作可以被同一線(xiàn)程重復(fù)執(zhí)行多次,多次執(zhí)行l(wèi)ock后,只有執(zhí)行相同次數(shù)的unlock操作,變量才會(huì)被解鎖。lock和unlock必須成對(duì)出現(xiàn)。
4)如果對(duì)一個(gè)變量執(zhí)行l(wèi)ock操作,將會(huì)清空工作內(nèi)存中此變量的值,在執(zhí)行引擎使用這個(gè)變量之前需要重新執(zhí)行l(wèi)oad或assign操作初始化變量的值。
5)如果一個(gè)變量事先沒(méi)有被lock操作鎖定,則不允許對(duì)它執(zhí)行unlock操作;也不允許去unlock一個(gè)被其他線(xiàn)程鎖定的變量。
6)對(duì)一個(gè)變量執(zhí)行unlock操作之前,必須先把此變量同步到主內(nèi)存中(執(zhí)行store和write操作。
并發(fā)編程的可見(jiàn)性、原子性和有序性問(wèn)題
原子性
原子性指的是一個(gè)操作是不可中斷的,即使是在多線(xiàn)程環(huán)境下,一個(gè)操作一旦開(kāi)始就不會(huì)被其他線(xiàn)程影響。在java中,對(duì)基本數(shù)據(jù)類(lèi)型的變量的讀取和賦值操作是原子性操作有點(diǎn)要注意的是,對(duì)于32位系統(tǒng)的來(lái)說(shuō),long類(lèi)型數(shù)據(jù)和double類(lèi)型數(shù)據(jù)(對(duì)于基本數(shù)據(jù)類(lèi)型,byte,short,int,float,boolean,char讀寫(xiě)是原子操作),它們的讀寫(xiě)并非原子性的,也就是說(shuō)如果存在兩條線(xiàn)程同時(shí)對(duì)long類(lèi)型或者double類(lèi)型的數(shù)據(jù)進(jìn)行讀寫(xiě)是存在相互干擾的,因?yàn)閷?duì)于32位虛擬機(jī)來(lái)說(shuō),每次原子讀寫(xiě)是32位的,而long和double則是64位的存儲(chǔ)單元,這樣會(huì)導(dǎo)致一個(gè)線(xiàn)程在寫(xiě)時(shí),操作完前32位的原子操作后,輪到B線(xiàn)程讀取時(shí),恰好只讀取到了后32位的數(shù)據(jù),這樣可能會(huì)讀取到一個(gè)既非原值又不是線(xiàn)程修改值的變量,它可能是“半個(gè)變量”的數(shù)值,即64位數(shù)據(jù)被兩個(gè)線(xiàn)程分成了兩次讀取。但也不必太擔(dān)心,因?yàn)樽x取到“半個(gè)變量”的情況比較少見(jiàn),至少在目前的商用的虛擬機(jī)中,幾乎都把64位的數(shù)據(jù)的讀寫(xiě)操作作為原子操作來(lái)執(zhí)行,因此對(duì)于這個(gè)問(wèn)題不必太在意,知道這么回事即可
可見(jiàn)性
- 可見(jiàn)性指的是當(dāng)一個(gè)線(xiàn)程修改了某個(gè)共享變量的值,其他線(xiàn)程是否能夠馬上得知這個(gè)修改的值。對(duì)于串行程序來(lái)說(shuō),可見(jiàn)性是不存在的,因?yàn)槲覀冊(cè)谌魏我粋€(gè)操作中修改了某個(gè)變量的值,后續(xù)的操作中都能讀取這個(gè)變量值,并且是修改過(guò)的新值。
- 但在多線(xiàn)程環(huán)境中可就不一定了,前面我們分析過(guò),由于線(xiàn)程對(duì)共享變量的操作都是線(xiàn)程拷貝到各自的工作內(nèi)存進(jìn)行操作后才寫(xiě)回到主內(nèi)存中的,這就可能存在一個(gè)線(xiàn)程A修改了共享變量x的值,還未寫(xiě)回主內(nèi)存時(shí),另外一個(gè)線(xiàn)程B又對(duì)主內(nèi)存中同一個(gè)共享變量x進(jìn)行操作,但此時(shí)A線(xiàn)程工作內(nèi)存中共享變量x對(duì)線(xiàn)程B來(lái)說(shuō)并不可見(jiàn),這種工作內(nèi)存與主內(nèi)存同步延遲現(xiàn)象就造成了可見(jiàn)性問(wèn)題,另外指令重排以及編譯器優(yōu)化也可能導(dǎo)致可見(jiàn)性問(wèn)題,通過(guò)前面的分析,我們知道無(wú)論是編譯器優(yōu)化還是處理器優(yōu)化的重排現(xiàn)象,在多線(xiàn)程環(huán)境下,確實(shí)會(huì)導(dǎo)致程序輪序執(zhí)行的問(wèn)題,從而也就導(dǎo)致可見(jiàn)性問(wèn)題。
有序性
有序性是指對(duì)于單線(xiàn)程的執(zhí)行代碼,我們總是認(rèn)為代碼的執(zhí)行是按順序依次執(zhí)行的,這樣的理解并沒(méi)有毛病,畢竟對(duì)于單線(xiàn)程而言確實(shí)如此,但對(duì)于多線(xiàn)程環(huán)境,則可能出現(xiàn)亂序現(xiàn)象,因?yàn)槌绦蚓幾g成機(jī)器碼指令后可能會(huì)出現(xiàn)指令重排現(xiàn)象,重排后的指令與原指令的順未必一致,要明白的是,在Java程序中,倘若在本線(xiàn)程內(nèi),所有操作都視為有序行為,如果是多線(xiàn)程環(huán)境下,一個(gè)線(xiàn)程中觀(guān)察另外一個(gè)線(xiàn)程,所有操作都是無(wú)序的,前半句指的是單線(xiàn)程內(nèi)保證串行語(yǔ)義執(zhí)行的一致性,后半句則指指令重排現(xiàn)象和工作內(nèi)存與主內(nèi)存同步延遲現(xiàn)象。
JMM是如何解決原子性&可見(jiàn)性&有序性問(wèn)題
原子性問(wèn)題
除了JVM自身提供的對(duì)基本數(shù)據(jù)類(lèi)型讀寫(xiě)操作的原子性外,可以通過(guò) synchronized和Lock實(shí)現(xiàn)原子性。因?yàn)閟ynchronized和Lock能夠保證任一時(shí)刻只有一個(gè)線(xiàn)程訪(fǎng)問(wèn)該代碼塊。
可見(jiàn)性問(wèn)題
- volatile關(guān)鍵字保證可見(jiàn)性。當(dāng)一個(gè)共享變量被volatile修飾時(shí),它會(huì)保證修改的值立即被其他的線(xiàn)程看到,即修改的值立即更新到主存中,當(dāng)其他線(xiàn)程需要讀取時(shí),它會(huì)去內(nèi)存中讀取新值。
- synchronized和Lock也可以保證可見(jiàn)性,因?yàn)樗鼈兛梢员WC任一時(shí)刻只有一個(gè)線(xiàn)程能訪(fǎng)問(wèn)共享資源,并在其釋放鎖之前將修改的變量刷新到內(nèi)存中。
- 通過(guò)volatile可以保證有序性。
- 可以通過(guò)synchronized和Lock來(lái)保證有序性,很顯然,synchronized和Lock保證每個(gè)時(shí)刻是有一個(gè)線(xiàn)程執(zhí)行同步代碼,相當(dāng)于是讓線(xiàn)程順序執(zhí)行同步代碼,自然就保證了有序性。
指令重排序
Java語(yǔ)言規(guī)范規(guī)定JVM線(xiàn)程內(nèi)部維持順序化語(yǔ)義。即只要程序的最終結(jié)果與它的順序情況的結(jié)果相等,那么指令的執(zhí)行順序可以與代碼順序不一樣,此過(guò)程叫指令你的重排序
指令重排序的意義是什么?
JVM能根據(jù)處理器特征適當(dāng)?shù)膶?duì)機(jī)器指令進(jìn)行重排序,使機(jī)器指令能更符合CPU的執(zhí)行特性,最大限度的發(fā)揮機(jī)器特性。
as-if-serial 語(yǔ)義
不管怎么重排序,程序的執(zhí)行結(jié)果不能被改變。編譯器、runtime和處理器都必須遵守as-if-serial語(yǔ)義。
為了遵守as-if-serial 語(yǔ)義,編譯器和處理器不會(huì)對(duì)存在數(shù)據(jù)依賴(lài)關(guān)系的操作做重排序,因?yàn)檫@種重排序會(huì)改變執(zhí)行結(jié)果。但是操作之間不存在數(shù)據(jù)依賴(lài)關(guān)系,這些操作就可能被編譯器和處理器重排序。
happens-before原則
只靠synchronized和volatile關(guān)鍵字來(lái)保證原子性、可見(jiàn)性以及有序性,那么編寫(xiě)并發(fā)編程顯得十分麻煩,從JDK5開(kāi)始,Java使用新的JSR-133內(nèi)存模型,提供了happeds-before原則來(lái)輔助保證程序執(zhí)行的原子性、可見(jiàn)性以及有序性的問(wèn)題,它是判斷數(shù)據(jù)是否存在競(jìng)爭(zhēng)、線(xiàn)程是否安全的依據(jù)。
程序順序原則,即在一個(gè)線(xiàn)程內(nèi)必須保證語(yǔ)義串行性,也就是說(shuō)按照代碼順序執(zhí)行。
鎖規(guī)則:解鎖(unlock)操作必然發(fā)生在后續(xù)的同一個(gè)鎖的加鎖(lock)之前,也就是說(shuō),如果對(duì)于一個(gè)鎖解鎖后,再加鎖,那么加鎖的動(dòng)作必須在解鎖動(dòng)作之后(同一個(gè)鎖)。
volatile規(guī)則: volatile變量的寫(xiě),先發(fā)生于讀,這保證了volatile變量的可見(jiàn)性,簡(jiǎn)單的理解就是,volatile變量在每次被線(xiàn)程訪(fǎng)問(wèn)時(shí),都強(qiáng)迫從主內(nèi)存中讀該變量的值,而當(dāng)該變量發(fā)生變化時(shí),又會(huì)強(qiáng)迫將最新的值刷新到主內(nèi)存,任何時(shí)刻,不同的線(xiàn)程總是能夠看到該變量的最新值。
線(xiàn)程啟動(dòng)規(guī)則: 線(xiàn)程的start()方法先于它的每一個(gè)動(dòng)作,即如果線(xiàn)程A在執(zhí)行線(xiàn)程B的start方法之前修改了共享變量的值,那么當(dāng)線(xiàn)程B執(zhí)行start方法時(shí),線(xiàn)程A對(duì)共享變量的修改對(duì)線(xiàn)程B可見(jiàn)。
傳遞性 A先于B ,B先于C 那么A必然先于C
線(xiàn)程終止規(guī)則: 線(xiàn)程的所有操作先于線(xiàn)程的終結(jié),Thread.join()方法的作用是等待當(dāng)前執(zhí)行的線(xiàn)程終止。假設(shè)在線(xiàn)程B終止之前,修改了共享變量,線(xiàn)程A從線(xiàn)程B的join方法成功返回后,線(xiàn)程B對(duì)共享變量的修改將對(duì)線(xiàn)程A可見(jiàn)。
線(xiàn)程中斷規(guī)則: 對(duì)線(xiàn)程 interrupt()方法的調(diào)用先行發(fā)生于被中斷線(xiàn)程的代碼檢測(cè)到中斷事件的發(fā)生,可以通過(guò)Thread.interrupted()方法檢測(cè)線(xiàn)程是否中斷。
對(duì)象終結(jié)規(guī)則對(duì)象的構(gòu)造函數(shù)執(zhí)行,結(jié)束先于finalize()方法
總結(jié)
以上是生活随笔為你收集整理的Java 内存模型(JMM)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 关于快速开发和设计应用系统的一些个人的意
- 下一篇: firefox addons