必須要學(xué)會(huì)的JMM與volatile_第1頁
必須要學(xué)會(huì)的JMM與volatile_第2頁
必須要學(xué)會(huì)的JMM與volatile_第3頁
必須要學(xué)會(huì)的JMM與volatile_第4頁
必須要學(xué)會(huì)的JMM與volatile_第5頁
已閱讀5頁,還剩8頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第必須要學(xué)會(huì)的JMM與volatile目錄1.JAVA內(nèi)存模型(JMM)1.1主內(nèi)存與工作內(nèi)存1.2內(nèi)存間的交互2.關(guān)于Volatile變量3.關(guān)于內(nèi)存屏障4.原子性、可見性與有序性5.Happens-Before

1.JAVA內(nèi)存模型(JMM)

JMM是用來干嘛的?:《Java虛擬機(jī)規(guī)范》中曾試圖定義一種Java內(nèi)存模型(JavaMemoryModel,JMM)來屏蔽各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,以實(shí)現(xiàn)讓Java程序在各種平臺(tái)下都能達(dá)到一致的內(nèi)存訪問效果。主要目的是什么?:定義程序中各種變量的訪問規(guī)則,即關(guān)注在虛擬機(jī)中把變量值存儲(chǔ)到內(nèi)存和從內(nèi)存中取出變量值這樣的底層細(xì)節(jié)。此處的變量(Variables)與Java編程中所說的變量有所區(qū)別,它包括了實(shí)例字段、靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素,但是不包括局部變量與方法參數(shù),因?yàn)楹笳呤蔷€程私有的,不會(huì)被共享,自然就不會(huì)存在競(jìng)爭(zhēng)問題。

1.1主內(nèi)存與工作內(nèi)存

Java內(nèi)存模型規(guī)定了所有的變量都存儲(chǔ)在主內(nèi)存(MainMemory)中。每條線程還有自己的工作內(nèi)存(WorkingMemory),線程的工作內(nèi)存中保存了被該線程使用的變量的主內(nèi)存副本,線程對(duì)變量的所有操作(讀取、賦值等)都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫主內(nèi)存中的數(shù)據(jù)。不同的線程之間也無法直接訪問對(duì)方工作內(nèi)存中的變量,線程間變量值的傳遞均需要通過主內(nèi)存來完成。

這里所講的主內(nèi)存、工作內(nèi)存與Java內(nèi)存區(qū)域中的Java堆、棧、方法區(qū)等并不是同一個(gè)層次的對(duì)內(nèi)存的劃分,這兩者基本上是沒有任何關(guān)系的。如果兩者一定要勉強(qiáng)對(duì)應(yīng)起來,那么從變量、主內(nèi)存、工作內(nèi)存的定義來看,主內(nèi)存主要對(duì)應(yīng)于Java堆中的對(duì)象實(shí)例數(shù)據(jù)部分,而工作內(nèi)存則對(duì)應(yīng)于虛擬機(jī)棧中的部分區(qū)域。

從更基礎(chǔ)的層次上說,主內(nèi)存直接對(duì)應(yīng)于物理硬件的內(nèi)存,而為了獲取更好的運(yùn)行速度,虛擬機(jī)(或者是硬件、操作系統(tǒng)本身的優(yōu)化措施)可能會(huì)讓工作內(nèi)存優(yōu)先存儲(chǔ)于寄存器和高速緩存中,因?yàn)槌绦蜻\(yùn)行時(shí)主要訪問的是工作內(nèi)存。

1.2內(nèi)存間的交互

關(guān)于主內(nèi)存與工作內(nèi)存之間具體的交互協(xié)議,即一個(gè)變量如何從主內(nèi)存拷貝到工作內(nèi)存、如何從工作內(nèi)存同步回主內(nèi)存這一類的實(shí)現(xiàn)細(xì)節(jié),Java內(nèi)存模型中定義了以下8種操作來完成。Java虛擬機(jī)實(shí)現(xiàn)時(shí)必須保證下面提及的每一種操作都是原子的、不可再分的(對(duì)于double和long類型的變量來說,load、store、read和write操作在某些平臺(tái)上允許有例外)。

lock(鎖定):作用于主內(nèi)存的變量,它把一個(gè)變量標(biāo)識(shí)為一條線程獨(dú)占的狀態(tài)。unlock(解鎖):作用于主內(nèi)存的變量,它把一個(gè)處于鎖定狀態(tài)的變量釋放出來,釋放后的變量才可以被其他線程鎖定。read(讀?。鹤饔糜谥鲀?nèi)存的變量,它把一個(gè)變量的值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中,以便隨后的load動(dòng)作使用。load(載入):作用于工作內(nèi)存的變量,它把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中。use(使用):作用于工作內(nèi)存的變量,它把工作內(nèi)存中一個(gè)變量的值傳遞給執(zhí)行引擎,每當(dāng)虛擬機(jī)遇到一個(gè)需要使用變量的值的字節(jié)碼指令時(shí)將會(huì)執(zhí)行這個(gè)操作。assign(賦值):作用于工作內(nèi)存的變量,它把一個(gè)從執(zhí)行引擎接收的值賦給工作內(nèi)存的變量,每當(dāng)虛擬機(jī)遇到一個(gè)給變量賦值的字節(jié)碼指令時(shí)執(zhí)行這個(gè)操作。store(存儲(chǔ)):作用于工作內(nèi)存的變量,它把工作內(nèi)存中一個(gè)變量的值傳送到主內(nèi)存中,以便隨后的write操作使用。write(寫入):作用于主內(nèi)存的變量,它把store操作從工作內(nèi)存中得到的變量的值放入主內(nèi)存的變量中。

非原子性協(xié)定:

Java內(nèi)存模型要求lock、unlock、read、load、assign、use、store、write這八種操作都具有原子性,但是對(duì)于64位的數(shù)據(jù)類型(long和double),在模型中特別定義了一條寬松的規(guī)定:允許虛擬機(jī)將沒有被volatile修飾的64位數(shù)據(jù)的讀寫操作劃分為兩次32位的操作來進(jìn)行,即允許虛擬機(jī)實(shí)現(xiàn)自行選擇是否要保證64位數(shù)據(jù)類型的load、store、read和write這四個(gè)操作的原子性,這就是所謂的long和double的非原子性協(xié)定(Non-AtomicTreatmentofdoubleandlongVariables)。

如果要把一個(gè)變量從主內(nèi)存拷貝到工作內(nèi)存,那就要按順序執(zhí)行read和load操作,如果要把變量從工作內(nèi)存同步回主內(nèi)存,就要按順序執(zhí)行store和write操作。注意,Java內(nèi)存模型只要求上述兩個(gè)操作必須按順序執(zhí)行,但不要求是連續(xù)執(zhí)行。也就是說read與load之間、store與write之間是可插入其他指令的,如對(duì)主內(nèi)存中的變量a、b進(jìn)行訪問時(shí),一種可能出現(xiàn)的順序是reada、readb、loadb、loada。

Java內(nèi)存模型還規(guī)定了在執(zhí)行上述8種基本操作時(shí)必須滿足如下規(guī)則:

不允許read和load、store和write操作之一單獨(dú)出現(xiàn),即不允許一個(gè)變量從主內(nèi)存讀取了但工作內(nèi)存不接受,或者工作內(nèi)存發(fā)起回寫了但主內(nèi)存不接受的情況出現(xiàn)。不允許一個(gè)線程丟棄它最近的assign操作,即變量在工作內(nèi)存中改變了之后必須把該變化同步回主內(nèi)存。不允許一個(gè)線程無原因地(沒有發(fā)生過任何assign操作)把數(shù)據(jù)從線程的工作內(nèi)存同步回主內(nèi)存中。一個(gè)新的變量只能在主內(nèi)存中誕生,不允許在工作內(nèi)存中直接使用一個(gè)未被初始化(load或assign)的變量,換句話說就是對(duì)一個(gè)變量實(shí)施use、store操作之前,必須先執(zhí)行assign和load操作。一個(gè)變量在同一個(gè)時(shí)刻只允許一條線程對(duì)其進(jìn)行l(wèi)ock操作,但lock操作可以被同一條線程重復(fù)執(zhí)行多次,多次執(zhí)行l(wèi)ock后,只有執(zhí)行相同次數(shù)的unlock操作,變量才會(huì)被解鎖。如果對(duì)一個(gè)變量執(zhí)行l(wèi)ock操作,那將會(huì)清空工作內(nèi)存中此變量的值,在執(zhí)行引擎使用這個(gè)變量前,需要重新執(zhí)行l(wèi)oad或assign操作以初始化變量的值。如果一個(gè)變量事先沒有被lock操作鎖定,那就不允許對(duì)它執(zhí)行unlock操作,也不允許去unlock一個(gè)被其他線程鎖定的變量。對(duì)一個(gè)變量執(zhí)行unlock操作之前,必須先把此變量同步回主內(nèi)存中(執(zhí)行store、write操作)。

2.關(guān)于Volatile變量

Volatile變量具備的三個(gè)關(guān)鍵點(diǎn)(保證可見性,不能保證原子性,禁止指令重排序):

用volatile聲明一個(gè)變量可以保證對(duì)所有線程的可見性,這里的可見性是指當(dāng)一條線程修改了這個(gè)變量的值,新值對(duì)于其他線程來說是可以及時(shí)得知的(并不是立即可見的,從物理存儲(chǔ)的角度看,各個(gè)線程的工作內(nèi)存中volatile變量也可以存在不一致的情況,但由于每次使用之前都要先刷新,執(zhí)行引擎看不到不一致的情況,因此可以認(rèn)為不存在一致性問題))?;趘olatile變量的運(yùn)算在并發(fā)下并不是線程安全的。禁止指令重排序優(yōu)化,普通的變量?jī)H會(huì)保證在該方法的執(zhí)行過程中所有依賴賦值結(jié)果的地方都能獲取到正確的結(jié)果,而不能保證變量賦值操作的順序與程序代碼中的執(zhí)行順序一致。因?yàn)樵谕粋€(gè)線程的方法執(zhí)行過程中無法感知到這點(diǎn),這就是Java內(nèi)存模型中描述的所謂線程內(nèi)表現(xiàn)為串行的語義(As-If-Serial)。

對(duì)于As-If-Serial的簡(jiǎn)要說明:對(duì)于處理器或者編譯器來說,在進(jìn)行指令重排序優(yōu)化(為了提高并行度)的時(shí)候只能保證在單線程環(huán)境下的串行化語義的一致性。

舉個(gè)栗子,下面一個(gè)雙鎖檢測(cè)(DoubleCheckLock,DCL)單例:

publicclassSingleton{

privatevolatilestaticSingletoninstance;

publicstaticSingletongetInstance(){

if(instance==null){

synchronized(Singleton.class){

if(instance==null){

instance=newSingleton();

returninstance;

publicstaticvoidmain(String[]args){

Singleton.getInstance();

}

對(duì)instance變量賦值相關(guān)的字節(jié)碼:

0x01a3de0f:mov$0x3375cdb0,%esi;...beb0cd7533

;{oop('Singleton')}

0x01a3de14:mov%eax,0x150(%esi);...898650010000

0x01a3de1a:shr$0x9,%esi;...c1ee09

0x01a3de1d:movb$0x0,0x1104800(%esi);...c6860048100100

0x01a3de24:lockaddl$0x0,(%esp);...f083042400

;*putstaticinstance

;-Singleton::getInstance@24

有volatile修飾的變量,賦值后(前面mov%eax,0x150(%esi)這句便是賦值操作)多執(zhí)行了一個(gè)lockaddl$0x0,(%esp)操作,這個(gè)操作的作用相當(dāng)于一個(gè)內(nèi)存屏障(MemoryBarrier或MemoryFence,指重排序時(shí)不能把后面的指令重排序到內(nèi)存屏障之前的位置),只有一個(gè)處理器訪問內(nèi)存時(shí),并不需要內(nèi)存屏障;但如果有兩個(gè)或更多處理器訪問同一塊內(nèi)存,且其中有一個(gè)在觀測(cè)另一個(gè),就需要內(nèi)存屏障來保證一致性了。

這句指令中的addl$0x0,(%esp)(把ESP寄存器的值加0)顯然是一個(gè)空操作,之所以用這個(gè)空操作而不是空操作專用指令nop,是因?yàn)镮A32手冊(cè)規(guī)定lock前綴不允許配合nop指令使用。這里的關(guān)鍵在于lock前綴,查詢IA32手冊(cè)可知,它的作用是將本處理器的緩存寫入了內(nèi)存,該寫入動(dòng)作也會(huì)引起別的處理器或者別的內(nèi)核無效化(Invalidate)其緩存,這種操作相當(dāng)于對(duì)緩存中的變量做了一次前面介紹Java內(nèi)存模式中所說的store和write操作。所以通過這樣一個(gè)空操作,可讓前面volatile變量的修改對(duì)其他處理器立即可見。

那為何說它禁止指令重排序呢?從硬件架構(gòu)上講,指令重排序是指處理器采用了允許將多條指令不按程序規(guī)定的順序分開發(fā)送給各個(gè)相應(yīng)的電路單元進(jìn)行處理。但并不是說指令任意重排,處理器必須能正確處理指令依賴情況保障程序能得出正確的執(zhí)行結(jié)果。譬如指令1把地址A中的值加10,指令2把地址A中的值乘以2,指令3把地址B中的值減去3,這時(shí)指令1和指令2是有依賴的,它們之間的順序不能重排(A+10)*2與A*2+10顯然不相等,但指令3可以重排到指令1、2之前或者中間,只要保證處理器執(zhí)行后面依賴到A、B值的操作時(shí)能獲取正確的A和B值即可。所以在同一個(gè)處理器中,重排序過的代碼看起來依然是有序的。因此,lockaddl$0x0,(%esp)指令把修改同步到內(nèi)存時(shí),意味著所有之前的操作都已經(jīng)執(zhí)行完成,這樣便形成了指令重排序無法越過內(nèi)存屏障的效果。

假定T表示一個(gè)線程,V和W分別表示兩個(gè)volatile型變量,那么在進(jìn)行read、load、use、assign、store和write操作時(shí)需要滿足如下對(duì)于volatile變量的特殊規(guī)則:

只有當(dāng)線程T對(duì)變量V執(zhí)行的前一個(gè)動(dòng)作是load的時(shí)候,線程T才能對(duì)變量V執(zhí)行use動(dòng)作;并且,只有當(dāng)線程T對(duì)變量V執(zhí)行的后一個(gè)動(dòng)作是use的時(shí)候,線程T才能對(duì)變量V執(zhí)行l(wèi)oad動(dòng)作。線程T對(duì)變量V的use動(dòng)作可以認(rèn)為是和線程T對(duì)變量V的load、read動(dòng)作相關(guān)聯(lián)的,必須連續(xù)且一起出現(xiàn)。(及時(shí)得到volatile變量的新值)只有當(dāng)線程T對(duì)變量V執(zhí)行的前一個(gè)動(dòng)作是assign的時(shí)候,線程T才能對(duì)變量V執(zhí)行store動(dòng)作;并且,只有當(dāng)線程T對(duì)變量V執(zhí)行的后一個(gè)動(dòng)作是store的時(shí)候,線程T才能對(duì)變量V執(zhí)行assign動(dòng)作。線程T對(duì)變量V的assign動(dòng)作可以認(rèn)為是和線程T對(duì)變量V的store、write動(dòng)作相關(guān)聯(lián)的,必須連續(xù)且一起出現(xiàn)。(及時(shí)將volatile變量的改動(dòng)同步到主存)假定動(dòng)作A是線程T對(duì)變量V實(shí)施的use或assign動(dòng)作,假定動(dòng)作F是和動(dòng)作A相關(guān)聯(lián)的load或store動(dòng)作,假定動(dòng)作P是和動(dòng)作F相應(yīng)的對(duì)變量V的read或write動(dòng)作;與此類似,假定動(dòng)作B是線程T對(duì)變量W實(shí)施的use或assign動(dòng)作,假定動(dòng)作G是和動(dòng)作B相關(guān)聯(lián)的load或store動(dòng)作,假定動(dòng)作Q是和動(dòng)作G相應(yīng)的對(duì)變量W的read或write動(dòng)作。如果A先于B,那么P先于Q。(禁止指令重排序優(yōu)化)

3.關(guān)于內(nèi)存屏障

內(nèi)存屏障又稱內(nèi)存柵欄(MemoryBarrier)是一個(gè)CPU指令,它的作用有兩個(gè):

保證特定操作的執(zhí)行順序保證某些變量的內(nèi)存可見性(利用該特性實(shí)現(xiàn)volatile的內(nèi)存可見性)

由于編譯器和處理器都能執(zhí)行指令重排優(yōu)化。如果在指令間插入一條MemoryBarrier則會(huì)告訴編譯器和CPU,不管什么指令都不能和這條MemoryBarrier指令重排序,也就是說通過插入內(nèi)存屏障禁止在內(nèi)存屏障前后的指令執(zhí)行重排序優(yōu)化。MemoryBarrier的另外一個(gè)作用是強(qiáng)制刷出各種CPU的緩存數(shù)據(jù),因此任何CPU。上的線程都能讀取到這些數(shù)據(jù)的最新版本??傊畍olatile變量正是通過內(nèi)存屏障實(shí)現(xiàn)其在內(nèi)存中的語義,即可見性和禁止重排優(yōu)化。

Intel硬件提供了一系列的內(nèi)存屏障,主要有:

Ifence,是一種LoadBarrier讀屏障sfence,是一種StoreBarrier寫屏障mfence,是一種全能型的屏障,具備ifence和sfence的能力Lock前綴,Lock不是一種內(nèi)存屏障,但是它能完成類似內(nèi)存屏障的功能。Lock會(huì)對(duì)CPU總線和高速緩存加鎖,可以理解為CPU指令級(jí)的一種鎖。它后面可以跟ADD,ADC,AND,BTC,BTR,BTS,CMPXCHG,CMPXCH8B,DEC,INC,NEG,NOT,OR,SBB,SUB,XOR,XADD,andXCHG等指令。

不同硬件實(shí)現(xiàn)內(nèi)存屏障的方式不同,Java內(nèi)存模型屏蔽了這種底層硬件平臺(tái)的差異,由JVM來為不同的平臺(tái)生成相應(yīng)的機(jī)器碼。

JVM中提供了四類內(nèi)存屏障指令:

volatile內(nèi)存語義的實(shí)現(xiàn):

總的來說:

當(dāng)?shù)谝粋€(gè)操作是volatile讀時(shí),不管第二個(gè)操作是什么,都不能重排序。這個(gè)規(guī)則確保volatile讀之后的操作不會(huì)被編譯器重排序到volatile讀之前。當(dāng)?shù)诙€(gè)操作是volatile寫時(shí),不管第一個(gè)操作是什么,都不能重排序。這個(gè)規(guī)則確保volatile寫之前的操作不會(huì)被編譯器重排序到volatile寫之后。當(dāng)?shù)谝粋€(gè)操作是volatile寫,第二個(gè)操作是volatile讀或?qū)憰r(shí),不能重排序。

為了實(shí)現(xiàn)volatile的內(nèi)存語義,編譯器在生成字節(jié)碼時(shí),會(huì)在指令序列中插入內(nèi)存屏障來禁止特定類型的處理器重排序。對(duì)于編譯器來說,發(fā)現(xiàn)一個(gè)最優(yōu)布置來最小化插入屏障的總數(shù)幾乎不可能。為此,JMM采取保守策略

下面是基于保守策略的JMM內(nèi)存屏障插入策略:

在每個(gè)volatile寫操作的前面插入一個(gè)StoreStore屏障。在每個(gè)volatile寫操作的后面插入一個(gè)StoreLoad屏障。在每個(gè)volatile讀操作的后面插入一個(gè)LoadLoad屏障。在每個(gè)volatile讀操作的后面插入一個(gè)LoadStore屏障。

上述內(nèi)存屏障插入策略非常保守,但它可以保證在任意處理器平臺(tái),任意的程序中都能得到正確的volatile內(nèi)存語義。

上圖中StoreStore屏障可以保證在volatile寫之前,其前面的所有普通寫操作已經(jīng)對(duì)任意處理器可見了。這是因?yàn)镾toreStore屏障將保障上面所有的普通寫在volatile寫之前刷新到主內(nèi)存。

上圖中StoreLoad屏障的作用是避免volatile寫與后面可能有的volatile讀/寫操作重排序。因?yàn)榫幾g器常常無法準(zhǔn)確判斷在一個(gè)volatile寫的后面否需要插入一個(gè)StoreLoad屏障(比如,一個(gè)volatile寫之后方法立即return)。為了保證能正確實(shí)現(xiàn)volatile的內(nèi)存語義,JMM在采取了保守策略:在每個(gè)volatile寫的后面,或者在每個(gè)volatile讀的前面插入一個(gè)StoreLoad屏障。

從整體執(zhí)行效率的角度考慮,JMM最終選擇了在每個(gè)volatile寫的后面插入一個(gè)StoreLoad屏障。因?yàn)関olatile寫-讀內(nèi)存語義的常見使用模式是:一個(gè)寫線程寫volatile變量,多個(gè)讀線程讀同一個(gè)volatile變量。當(dāng)讀線程的數(shù)量大大超過寫線程時(shí),選擇在volatile寫之后插入StoreLoad屏障將帶來可觀的執(zhí)行效率的提升。從這里可以看到JMM在實(shí)現(xiàn)上的一個(gè)特點(diǎn):首先確保正確性,然后再去追求執(zhí)行效率。

上圖中LoadLoad屏障用來禁止處理器把上面的volatile讀與下面的普通讀重排序。LoadStore屏障用來禁止處理器把上面的volatile讀與下面的普通寫重排序。

classVolatileBarrierExample{

inta;

volatileintv1=1;

volatileintv2=2;

voidreadAndwrite(){

inti=v1;//第一個(gè)volatile讀

intj=v2//第二個(gè)volatile讀

a=i+j;//普通寫

v1=i+1;//第一個(gè)volatile寫

v2=j*2;//第二個(gè)volatile寫

}

針對(duì)readAndWrite()方法,編譯器在生成字節(jié)碼時(shí)可以做如下的優(yōu)化。

注意,最后的StoreLoad屏障不能省略。因?yàn)榈诙€(gè)volatile寫之后,方法立即return。此時(shí)編譯器可能無法準(zhǔn)確斷定后面是否會(huì)有volatile讀或?qū)?,為了安全起見編譯器通常會(huì)在這里插入一個(gè)StoreLoad屏障。上面的優(yōu)化針對(duì)任意處理器平臺(tái),由于不同的處理器有不同松緊度的處理器內(nèi)存模型,內(nèi)存屏障的插入還可以根據(jù)具體的處理器內(nèi)存模型繼續(xù)優(yōu)化。

4.原子性、可見性與有序性

保證原子性(Atomicity):

JMM定義的原子操作:JMM來直接保證的原子性變量的操作包括read、load、assign、use、store和write這六個(gè)。synchronized關(guān)鍵字:Java內(nèi)存模型還提供了lock和unlock操作來滿足更大范圍的原子性保證,盡管虛擬機(jī)未把lock和unlock操作直接開放給用戶使用,但是卻提供了更高層次的字節(jié)碼指令monitorenter和monitorexit來隱式地使用這兩個(gè)操作。反映到Java代碼中就是同步塊synchronized關(guān)鍵字,因此在synchronized塊之間的操作也具備原子性。

保證可見性(Visibility):

volatile關(guān)鍵字:JMM對(duì)于volatile定義的特殊規(guī)則保證了新值能立即同步到主內(nèi)存,以及每次使用前立即從主內(nèi)存刷新來保證可見性的。synchronized關(guān)鍵字:同步塊的可見性是由對(duì)一個(gè)變量執(zhí)行unlock操作之前,必須先把此變量同步回主內(nèi)存中(執(zhí)行store、write操作)這條規(guī)則獲得的。final關(guān)鍵字:被final修飾的字段在構(gòu)造器中一旦被初始化完成,并且構(gòu)造器沒有把this的引用傳遞出去(this引用逃逸是一件很危險(xiǎn)的事情,其他線程有可能通過這個(gè)引用訪問到初始化了一半的對(duì)象),那么在其他線程中就能看見final字段的值。

保證有序性(Ordering):

As-If-Serial:如果在本線程內(nèi)觀察,所有的操作都是有序的。volatile關(guān)鍵字:volatile關(guān)鍵字本身就包含了禁止指令重排序的語義,在具體的實(shí)現(xiàn)中(依賴內(nèi)存屏障)就會(huì)保證指令的有序性。synchronized關(guān)鍵字:一個(gè)變量在同一個(gè)時(shí)刻只允許一條線程對(duì)其進(jìn)行l(wèi)ock操作這條規(guī)則決定了持有同一個(gè)鎖的兩個(gè)同步塊只能串行地進(jìn)入而保證執(zhí)行時(shí)的有序性。

5.Happens-Before

先行發(fā)生是(Happens-Before)Java內(nèi)存模型中定義的兩項(xiàng)操作之間的偏序關(guān)系,比如說操作A先行發(fā)生于操作B,其實(shí)就是說在發(fā)生操作B之前,操作A產(chǎn)生的影響能被操作B觀察到,影響包括修改了內(nèi)存中共享變量的值、發(fā)送了消息、調(diào)用了方法等。(先行發(fā)生原則是JMM的實(shí)現(xiàn)所體現(xiàn)出的一些特定的現(xiàn)象)Java語言無須任何同步手段保障就能成立的先行發(fā)生規(guī)則有且只有下面這些:

程序次序規(guī)則(ProgramOrderRule):在一個(gè)線程內(nèi),按照控制流順序,書寫在前面的操作先行發(fā)生于書寫在后面的操作。注意,這里說的是控制流順序而不是程序代碼順序,因?yàn)橐紤]分支、循環(huán)等結(jié)構(gòu)。管程鎖定規(guī)則(MonitorLockRule):一個(gè)unlock操作先行發(fā)生于后面對(duì)同一個(gè)鎖的lock操作。這里必須強(qiáng)調(diào)的是同一個(gè)鎖,而后面是指時(shí)間上的先后。volatile變量規(guī)則(VolatileVariableRule):對(duì)一個(gè)volatile變量的寫操作先行發(fā)生于后面對(duì)這個(gè)變量的讀操作,這里的后面同樣是指時(shí)間上的先后。線程啟動(dòng)規(guī)則(ThreadStartRule):Thread對(duì)象的start()方法先行發(fā)生于此線程的每一個(gè)動(dòng)作。線程終止

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論