JMM和底層實現(xiàn)原理

1.并發(fā)編程領(lǐng)域的關(guān)鍵問題

1.1 線程之間的通信

線程的通信是指線程之間以何種機制來交換信息垮抗。在編程中,線程之間的通信機制有兩種达址,共享內(nèi)存和消息傳遞缠捌。
在共享內(nèi)存的并發(fā)模型里,線程之間共享程序的公共狀態(tài),線程之間通過寫-讀內(nèi)存中的公共狀態(tài)來隱式進(jìn)行通信银觅,典型的共享內(nèi)存通信方式就是通過共享對象進(jìn)行通信。
在消息傳遞的并發(fā)模型里位岔,線程之間沒有公共狀態(tài),線程之間必須通過明確的發(fā)送消息來顯式進(jìn)行通信署辉,在java中典型的消息傳遞方式就是wait()和notify()。

1.2 線程間的同步

同步是指程序用于控制不同線程之間操作發(fā)生相對順序的機制。
在共享內(nèi)存并發(fā)模型里氏身,同步是顯式進(jìn)行的。程序員必須顯式指定某個方法或某段代碼需要在線程之間互斥執(zhí)行骗灶。
在消息傳遞的并發(fā)模型里叙淌,由于消息的發(fā)送必須在消息的接收之前机杜,因此同步是隱式進(jìn)行的。

2.Java內(nèi)存模型——JMM

  • Java的并發(fā)采用的是共享內(nèi)存模型

2.1 現(xiàn)代計算機的內(nèi)存模型

物理計算機中的并發(fā)問題获黔,物理機遇到的并發(fā)問題與虛擬機中的情況有不少相似之處,物理機對并發(fā)的處理方案對于虛擬機的實現(xiàn)也有相當(dāng)大的參考意義盏触。

其中一個重要的復(fù)雜性來源是絕大多數(shù)的運算任務(wù)都不可能只靠處理器“計算”就能完成雌芽,處理器至少要與內(nèi)存交互世落,如讀取運算數(shù)據(jù)、存儲運算結(jié)果等武花,這個I/O操作是很難消除的(無法僅靠寄存器來完成所有運算任務(wù))。早期計算機中cpu和內(nèi)存的速度是差不多的干旁,但在現(xiàn)代計算機中争群,cpu的指令速度遠(yuǎn)超內(nèi)存的存取速度,由于計算機的存儲設(shè)備與處理器的運算速度有幾個數(shù)量級的差距,所以現(xiàn)代計算機系統(tǒng)都不得不加入一層讀寫速度盡可能接近處理器運算速度的高速緩存(Cache)來作為內(nèi)存與處理器之間的緩沖:將運算需要使用到的數(shù)據(jù)復(fù)制到緩存中轻要,讓運算能快速進(jìn)行冲泥,當(dāng)運算結(jié)束后再從緩存同步回內(nèi)存之中,這樣處理器就無須等待緩慢的內(nèi)存讀寫了嚼酝。

基于高速緩存的存儲交互很好地解決了處理器與內(nèi)存的速度矛盾闽巩,但是也為計算機系統(tǒng)帶來更高的復(fù)雜度延刘,因為它引入了一個新的問題:緩存一致性(Cache Coherence)碘赖。在多處理器系統(tǒng)中,每個處理器都有自己的高速緩存撼班,而它們又共享同一主內(nèi)存(MainMemory)砰嘁。當(dāng)多個處理器的運算任務(wù)都涉及同一塊主內(nèi)存區(qū)域時,將可能導(dǎo)致各自的緩存數(shù)據(jù)不一致缅阳,舉例說明變量在多個CPU之間的共享十办。如果真的發(fā)生這種情況,那同步回到主內(nèi)存時以誰的緩存數(shù)據(jù)為準(zhǔn)呢件相?為了解決一致性的問題适肠,需要各個處理器訪問緩存時都遵循一些協(xié)議澄干,在讀寫時要根據(jù)協(xié)議來進(jìn)行操作,這類協(xié)議有MSI、MESI(Illinois Protocol)逞泄、MOSI喷众、Synapse到千、Firefly及Dragon Protocol等。


  • 該內(nèi)存模型帶來的問題
    現(xiàn)代的處理器使用寫緩沖區(qū)臨時保存向內(nèi)存寫入的數(shù)據(jù)了赵。寫緩沖區(qū)可以保證指令流水線持續(xù)運行,它可以避免由于處理器停頓下來等待向內(nèi)存寫入數(shù)據(jù)而產(chǎn)生的延遲。同時鸠窗,通過以批處理的方式刷新寫緩沖區(qū),以及合并寫緩沖區(qū)中對同一內(nèi)存地址的多次寫净刮,減少對內(nèi)存總線的占用淹父。雖然寫緩沖區(qū)有這么多好處困介,但每個處理器上的寫緩沖區(qū),僅僅對它所在的處理器可見根穷。這個特性會對內(nèi)存操作的執(zhí)行順序產(chǎn)生重要的影響:處理器對內(nèi)存的讀/寫操作的執(zhí)行順序,不一定與內(nèi)存實際發(fā)生的讀/寫操作順序一致管引!
    處理器A和處理器B按程序的順序并行執(zhí)行內(nèi)存訪問,最終可能得到x=y=0的結(jié)果重慢。
    處理器A和處理器B可以同時把共享變量寫入自己的寫緩沖區(qū)(A1似踱,B1)酵熙,然后從內(nèi)存中讀取另一個共享變量(A2哮独,B2)分飞,最后才把自己寫緩存區(qū)中保存的臟數(shù)據(jù)刷新到內(nèi)存中(A3惨寿,B3)裂垦。當(dāng)以這種時序執(zhí)行時,程序就可以得到x=y=0的結(jié)果诚亚。
    從內(nèi)存操作實際發(fā)生的順序來看晕换,直到處理器A執(zhí)行A3來刷新自己的寫緩存區(qū),寫操作A1才算真正執(zhí)行了站宗。雖然處理器A執(zhí)行內(nèi)存操作的順序為:A1→A2闸准,但內(nèi)存操作實際發(fā)生的順序卻是A2→A1。

2.2 Java內(nèi)存模型(JMM)

JMM定義了Java 虛擬機(JVM)在計算機內(nèi)存(RAM)中的工作方式梢灭。JVM是整個計算機虛擬模型夷家,所以JMM是隸屬于JVM的钥顽。從抽象的角度來看奶浦,JMM定義了線程和主內(nèi)存之間的抽象關(guān)系:線程之間的共享變量存儲在主內(nèi)存(Main Memory)中扎瓶,每個線程都有一個私有的本地內(nèi)存(Local Memory)继薛,本地內(nèi)存中存儲了該線程以讀/寫共享變量的副本譬巫。本地內(nèi)存是JMM的一個抽象概念,并不真實存在墅诡。它涵蓋了緩存、寫緩沖區(qū)、寄存器以及其他的硬件和編譯器優(yōu)化致份。


2.2.1 JVM對Java內(nèi)存模型的實現(xiàn)

  • 在JVM內(nèi)部芳悲,Java內(nèi)存模型把內(nèi)存分成了兩部分:線程棧區(qū)和堆區(qū)
    JVM中運行的每個線程都擁有自己的線程棧,線程棧包含了當(dāng)前線程執(zhí)行的方法調(diào)用相關(guān)信息意乓,我們也把它稱作調(diào)用棧慢显。隨著代碼的不斷執(zhí)行纽疟,調(diào)用棧會不斷變化炎功。


    image.png

所有原始類型(boolean,byte,short,char,int,long,float,double)的局部變量都直接保存在線程棧當(dāng)中穆壕,對于它們的值各個線程之間都是獨立的缴允。對于原始類型的局部變量,一個線程可以傳遞一個副本給另一個線程,當(dāng)它們之間是無法共享的若治。
堆區(qū)包含了Java應(yīng)用創(chuàng)建的所有對象信息,不管對象是哪個線程創(chuàng)建的节槐,其中的對象包括原始類型的封裝類(如Byte、Integer、Long等等)。不管對象是屬于一個成員變量還是方法中的局部變量,它都會被存儲在堆區(qū)岗喉。
一個局部變量如果是原始類型胡岔,那么它會被完全存儲到棧區(qū)靶瘸。 一個局部變量也有可能是一個對象的引用,這種情況下毛肋,這個本地引用會被存儲到棧中怨咪,但是對象本身仍然存儲在堆區(qū)。
對于一個對象的成員方法润匙,這些方法中包含局部變量诗眨,仍需要存儲在棧區(qū),即使它們所屬的對象在堆區(qū)孕讳。 對于一個對象的成員變量匠楚,不管它是原始類型還是包裝類型,都會被存儲到堆區(qū)厂财。Static類型的變量以及類本身相關(guān)信息都會隨著類本身存儲在堆區(qū)芋簿。


2.3 Java內(nèi)存模型帶來的問題

2.3.1 可見性問題

CPU中運行的線程從主存中拷貝共享對象obj到它的CPU緩存,把對象obj的count變量改為2蟀苛。但這個變更對運行在右邊CPU中的線程不可見益咬,因為這個更改還沒有flush到主存中:要解決共享對象可見性這個問題,我們可以使用java volatile關(guān)鍵字或者是加鎖


2.3.2 競爭現(xiàn)象

線程A和線程B共享一個對象obj帜平。假設(shè)線程A從主存讀取Obj.count變量到自己的CPU緩存,同時梅鹦,線程B也讀取了Obj.count變量到它的CPU緩存裆甩,并且這兩個線程都對Obj.count做了加1操作。此時齐唆,Obj.count加1操作被執(zhí)行了兩次嗤栓,不過都在不同的CPU緩存中。如果這兩個加1操作是串行執(zhí)行的箍邮,那么Obj.count變量便會在原始值上加2茉帅,最終主存中的Obj.count的值會是3。然而下圖中兩個加1操作是并行的锭弊,不管是線程A還是線程B先flush計算結(jié)果到主存堪澎,最終主存中的Obj.count只會增加1次變成2,盡管一共有兩次加1操作味滞。 要解決上面的問題我們可以使用java synchronized代碼塊樱蛤。


2.4 Java內(nèi)存模型中的重排序

  • 在執(zhí)行程序時钮呀,為了提高性能,編譯器和處理器常常會對指令做重排序昨凡。

2.4.1 重排序類型

  • 1)編譯器優(yōu)化的重排序爽醋。編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執(zhí)行順序便脊。
  • 2)指令級并行的重排序÷焖模現(xiàn)代處理器采用了指令級并行技術(shù)(Instruction-LevelParallelism,ILP)來將多條指令重疊執(zhí)行哪痰。如果不存在數(shù)據(jù)依賴性遂赠,處理器可以改變語句對應(yīng)機器指令的執(zhí)行順序。
  • 3)內(nèi)存系統(tǒng)的重排序妒御。由于處理器使用緩存和讀/寫緩沖區(qū)解愤,這使得加載和存儲操作看上去可能是在亂序執(zhí)行。

2.4.2 重排序與依賴性

  • 數(shù)據(jù)依賴性
    如果兩個操作訪問同一個變量乎莉,且這兩個操作中有一個為寫操作送讲,此時這兩個操作之間就存在數(shù)據(jù)依賴性。數(shù)據(jù)依賴分為下列3種類型惋啃,這3種情況哼鬓,只要重排序兩個操作的執(zhí)行順序,程序的執(zhí)行結(jié)果就會被改變边灭。


  • 控制依賴性
    flag變量是個標(biāo)記异希,用來標(biāo)識變量a是否已被寫入,在use方法中比變量i依賴if (flag)的判斷绒瘦,這里就叫控制依賴称簿,如果發(fā)生了重排序,結(jié)果就不對了惰帽。


  • as-if-serial
    不管如何重排序憨降,都必須保證代碼在單線程下的運行正確,連單線程下都無法正確该酗,更不用討論多線程并發(fā)的情況授药,所以就提出了一個as-if-serial的概念。
    as-if-serial語義的意思是:不管怎么重排序(編譯器和處理器為了提高并行度)呜魄,(單線程)程序的執(zhí)行結(jié)果不能被改變悔叽。編譯器、runtime和處理器都必須遵守as-if-serial語義爵嗅。為了遵守as-if-serial語義娇澎,編譯器和處理器不會對存在數(shù)據(jù)依賴關(guān)系的操作做重排序,因為這種重排序會改變執(zhí)行結(jié)果操骡。(強調(diào)一下九火,這里所說的數(shù)據(jù)依賴性僅針對單個處理器中執(zhí)行的指令序列和單個線程中執(zhí)行的操作赚窃,不同處理器之間和不同線程之間的數(shù)據(jù)依賴性不被編譯器和處理器考慮。)但是岔激,如果操作之間不存在數(shù)據(jù)依賴關(guān)系勒极,這些操作依然可能被編譯器和處理器重排序。



    1和3之間存在數(shù)據(jù)依賴關(guān)系虑鼎,同時2和3之間也存在數(shù)據(jù)依賴關(guān)系辱匿。因此在最終執(zhí)行的指令序列中,3不能被重排序到1和2的前面(3排到1和2的前面炫彩,程序的結(jié)果將會被改變)匾七。但1和2之間沒有數(shù)據(jù)依賴關(guān)系,編譯器和處理器可以重排序1和2之間的執(zhí)行順序江兢。
    asif-serial語義使單線程下無需擔(dān)心重排序的干擾昨忆,也無需擔(dān)心內(nèi)存可見性問題。

2.4.3 并發(fā)下重排序帶來的問題


這里假設(shè)有兩個線程A和B杉允,A首先執(zhí)行init ()方法邑贴,隨后B線程接著執(zhí)行use ()方法。線程B在執(zhí)行操作4時叔磷,能否看到線程A在操作1對共享變量a的寫入呢拢驾?答案是:不一定能看到。
由于操作1和操作2沒有數(shù)據(jù)依賴關(guān)系改基,編譯器和處理器可以對這兩個操作重排序繁疤;同樣,操作3和操作4沒有數(shù)據(jù)依賴關(guān)系秕狰,編譯器和處理器也可以對這兩個操作重排序稠腊。讓我們先來看看,當(dāng)操作1和操作2重排序時鸣哀,可能會產(chǎn)生什么效果麻养?操作1和操作2做了重排序。程序執(zhí)行時诺舔,線程A首先寫標(biāo)記變量flag,隨后線程B讀這個變量备畦。由于條件判斷為真低飒,線程B將讀取變量a。此時懂盐,變量a還沒有被線程A寫入褥赊,這時就會發(fā)生錯誤!
當(dāng)操作3和操作4重排序時會產(chǎn)生什么效果莉恼?
在程序中拌喉,操作3和操作4存在控制依賴關(guān)系速那。當(dāng)代碼中存在控制依賴性時,會影響指令序列執(zhí)行的并行度尿背。為此端仰,編譯器和處理器會采用猜測(Speculation)執(zhí)行來克服控制相關(guān)性對并行度的影響。以處理器的猜測執(zhí)行為例田藐,執(zhí)行線程B的處理器可以提前讀取并計算a*a荔烧,然后把計算結(jié)果臨時保存到一個名為重排序緩沖(Reorder Buffer,ROB)的硬件緩存中汽久。當(dāng)操作3的條件判斷為真時鹤竭,就把該計算結(jié)果寫入變量i中。猜測執(zhí)行實質(zhì)上對操作3和4做了重排序景醇,問題在于這時候臀稚,a的值還沒被線程A賦值。在單線程程序中三痰,對存在控制依賴的操作重排序吧寺,不會改變執(zhí)行結(jié)果(這也是as-if-serial語義允許對存在控制依賴的操作做重排序的原因);但在多線程程序中酒觅,對存在控制依賴的操作重排序撮执,可能會改變程序的執(zhí)行結(jié)果。

2.4.4 解決在并發(fā)下的問題

1)內(nèi)存屏障——禁止重排序


Java編譯器在生成指令序列的適當(dāng)位置會插入內(nèi)存屏障指令來禁止特定類型的處理器重排序舷丹,從而讓程序按我們預(yù)想的流程去執(zhí)行抒钱。
1、保證特定操作的執(zhí)行順序颜凯。
2谋币、影響某些數(shù)據(jù)(或則是某條指令的執(zhí)行結(jié)果)的內(nèi)存可見性。

編譯器和CPU能夠重排序指令症概,保證最終相同的結(jié)果蕾额,嘗試優(yōu)化性能。插入一條Memory Barrier會告訴編譯器和CPU:不管什么指令都不能和這條Memory Barrier指令重排序彼城。
Memory Barrier所做的另外一件事是強制刷出各種CPU cache诅蝶,如一個Write-Barrier(寫入屏障)將刷出所有在Barrier之前寫入 cache 的數(shù)據(jù),因此募壕,任何CPU上的線程都能讀取到這些數(shù)據(jù)的最新版本调炬。
JMM把內(nèi)存屏障指令分為4類,解釋表格舱馅,StoreLoad Barriers是一個“全能型”的屏障缰泡,它同時具有其他3個屏障的效果。現(xiàn)代的多處理器大多支持該屏障(其他類型的屏障不一定被所有處理器支持)代嗤。

2)臨界區(qū)(synchronized棘钞?)


臨界區(qū)內(nèi)的代碼可以重排序(但JMM不允許臨界區(qū)內(nèi)的代碼“逸出”到臨界區(qū)之外缠借,那樣會破壞監(jiān)視器的語義)。JMM會在退出臨界區(qū)和進(jìn)入臨界區(qū)這兩個關(guān)鍵時間點做一些特別處理宜猜,雖然線程A在臨界區(qū)內(nèi)做了重排序泼返,但由于監(jiān)視器互斥執(zhí)行的特性,這里的線程B根本無法“觀察”到線程A在臨界區(qū)內(nèi)的重排序宝恶。這種重排序既提高了執(zhí)行效率符隙,又沒有改變程序的執(zhí)行結(jié)果。

2.5 Happens-Before

用happens-before的概念來闡述操作之間的內(nèi)存可見性垫毙。在JMM中霹疫,如果一個操作執(zhí)行的結(jié)果需要對另一個操作可見,那么這兩個操作之間必須要存在happens-before關(guān)系 综芥。

兩個操作之間具有happens-before關(guān)系丽蝎,并不意味著前一個操作必須要在后一個操作之前執(zhí)行!happens-before僅僅要求前一個操作(執(zhí)行的結(jié)果)對后一個操作可見膀藐,且前一個操作按順序排在第二個操作之前(the first is visible to and ordered before the second) 屠阻。

1)如果一個操作happens-before另一個操作,那么第一個操作的執(zhí)行結(jié)果將對第二個操作可見额各,而且第一個操作的執(zhí)行順序排在第二個操作之前国觉。(對程序員來說)

2)兩個操作之間存在happens-before關(guān)系,并不意味著Java平臺的具體實現(xiàn)必須要按照happens-before關(guān)系指定的順序來執(zhí)行虾啦。如果重排序之后的執(zhí)行結(jié)果麻诀,與按happens-before關(guān)系來執(zhí)行的結(jié)果一致,那么這種重排序是允許的(對編譯器和處理器 來說)

在Java 規(guī)范提案中為讓大家理解內(nèi)存可見性的這個概念傲醉,提出了happens-before的概念來闡述操作之間的內(nèi)存可見性蝇闭。對應(yīng)Java程序員來說,理解happens-before是理解JMM的關(guān)鍵硬毕。JMM這么做的原因是:程序員對于這兩個操作是否真的被重排序并不關(guān)心呻引,程序員關(guān)心的是程序執(zhí)行時的語義不能被改變(即執(zhí)行結(jié)果不能被改變)。因此吐咳,happens-before關(guān)系本質(zhì)上和as-if-serial語義是一回事逻悠。as-if-serial語義保證單線程內(nèi)程序的執(zhí)行結(jié)果不被改變,happens-before關(guān)系保證正確同步的多線程程序的執(zhí)行結(jié)果不被改變韭脊。

  • Happens-Before規(guī)則-無需任何同步手段就可以保證的
    1)程序順序規(guī)則:一個線程中的每個操作蹂风,happens-before于該線程中的任意后續(xù)操作。
    2)監(jiān)視器鎖規(guī)則:對一個鎖的解鎖乾蓬,happens-before于隨后對這個鎖的加鎖。
    3)volatile變量規(guī)則:對一個volatile域的寫慎恒,happens-before于任意后續(xù)對這個volatile域的讀任内。
    4)傳遞性:如果A happens-before B撵渡,且B happens-before C,那么A happens-before C死嗦。
    5)start()規(guī)則:如果線程A執(zhí)行操作ThreadB.start()(啟動線程B)趋距,那么A線程的ThreadB.start()操作happens-before于線程B中的任意操作。
    6)join()規(guī)則:如果線程A執(zhí)行操作ThreadB.join()并成功返回越除,那么線程B中的任意操作happens-before于線程A從ThreadB.join()操作成功返回节腐。
    7 )線程中斷規(guī)則:對線程interrupt方法的調(diào)用happens-before于被中斷線程的代碼檢測到中斷事件的發(fā)生。

3.實現(xiàn)原理

  • 內(nèi)存語義:可以簡單理解為 volatile摘盆,synchronize翼雀,atomic,lock 之類的在 JVM 中的內(nèi)存方面實現(xiàn)原則

3.1 volatile的內(nèi)存語義

volatile變量自身具有下列特性:

  • 可見性孩擂。對一個volatile變量的讀狼渊,總是能看到(任意線程)對這個volatile變量最后的寫入。
  • 原子性:對任意單個volatile變量的讀/寫具有原子性类垦,但類似于volatile++這種復(fù)合操作不具有原子性狈邑。

volatile寫的內(nèi)存語義如下:當(dāng)寫一個volatile變量時,JMM會把該線程對應(yīng)的本地內(nèi)存中的共享變量值刷新到主內(nèi)存蚤认。


volatile讀的內(nèi)存語義如下:當(dāng)讀一個volatile變量時米苹,JMM會把該線程對應(yīng)的本地內(nèi)存置為無效。線程接下來將從主內(nèi)存中讀取共享變量砰琢。


volatile重排序規(guī)則:


volatile內(nèi)存語義的實現(xiàn)——JMM對volatile的內(nèi)存屏障插入策略:
在每個volatile寫操作的前面插入一個StoreStore屏障蘸嘶。在每個volatile寫操作的后面插入一個StoreLoad屏障。
在每個volatile讀操作的后面插入一個LoadLoad屏障氯析。在每個volatile讀操作的后面插入一個LoadStore屏障亏较。



3.1.1 volatile的實現(xiàn)原理

有volatile變量修飾的共享變量進(jìn)行寫操作的時候會使用CPU提供的Lock前綴指令:

  • 將當(dāng)前處理器緩存行的數(shù)據(jù)寫回到系統(tǒng)內(nèi)存
  • 這個寫回內(nèi)存的操作會使在其他CPU里緩存了該內(nèi)存地址的數(shù)據(jù)無效。

3.2 鎖的內(nèi)存語義

當(dāng)線程釋放鎖時掩缓,JMM會把該線程對應(yīng)的本地內(nèi)存中的共享變量刷新到主內(nèi)存中雪情。。
當(dāng)線程獲取鎖時你辣,JMM會把該線程對應(yīng)的本地內(nèi)存置為無效巡通。從而使得被監(jiān)視器保護的臨界區(qū)代碼必須從主內(nèi)存中讀取共享變量。



3.2.1 synchronized的實現(xiàn)原理

使用monitorenter和monitorexit指令實現(xiàn)的:

  • monitorenter指令是在編譯后插入到同步代碼塊的開始位置舍哄,而monitorexit是插入到方法結(jié)束處和異常處
  • 每個monitorenter必須有對應(yīng)的monitorexit與之配對
  • 任何對象都有一個monitor與之關(guān)聯(lián)宴凉,當(dāng)且一個monitor被持有后,它將處于鎖定狀態(tài)

鎖的存放位置:


3.2.2 了解各種鎖

鎖一共有4種狀態(tài)表悬,級別從低到高依次是:無鎖狀態(tài)弥锄、偏向鎖狀態(tài)、輕量級鎖狀態(tài)和重量級鎖狀態(tài)。

偏向鎖:大多數(shù)情況下籽暇,鎖不僅不存在多線程競爭温治,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價更低而引入了偏向鎖戒悠。無競爭時不需要進(jìn)行CAS操作來加鎖和解鎖熬荆。

輕量級鎖:無競爭時通過CAS操作來加鎖和解鎖。(自旋鎖——是一種鎖的機制绸狐,不是狀態(tài))

重量級鎖:真正的加鎖操作

3.3 final的內(nèi)存語義

編譯器和處理器要遵守兩個重排序規(guī)則:

  • 在構(gòu)造函數(shù)內(nèi)對一個final域的寫入卤恳,與隨后把這個被構(gòu)造對象的引用賦值給一個引用變量,這兩個操作之間不能重排序寒矿。
  • 初次讀一個包含final域的對象的引用突琳,與隨后初次讀這個final域,這兩個操作之間不能重排序劫窒。

final域為引用類型:

  • 增加了如下規(guī)則:在構(gòu)造函數(shù)內(nèi)對一個final引用的對象的成員域的寫入本今,與隨后在構(gòu)造函數(shù)外把這個被構(gòu)造對象的引用賦值給一個引用變量,這兩個操作之間不能重排序主巍。

final語義在處理器中的實現(xiàn):

  • 會要求編譯器在final域的寫之后冠息,構(gòu)造函數(shù)return之前插入一個StoreStore障屏。
  • 讀final域的重排序規(guī)則要求編譯器在讀final域的操作前面插入一個LoadLoad屏障

參考

  • 1)享學(xué)課堂Mark老師筆記
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末孕索,一起剝皮案震驚了整個濱河市逛艰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌搞旭,老刑警劉巖散怖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異肄渗,居然都是意外死亡镇眷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門翎嫡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來欠动,“玉大人,你說我怎么就攤上這事惑申【呶椋” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵圈驼,是天一觀的道長人芽。 經(jīng)常有香客問我,道長绩脆,這世上最難降的妖魔是什么萤厅? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任橄抹,我火速辦了婚禮,結(jié)果婚禮上祈坠,老公的妹妹穿的比我還像新娘害碾。我一直安慰自己,他們只是感情好赦拘,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著芬沉,像睡著了一般躺同。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上丸逸,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天蹋艺,我揣著相機與錄音,去河邊找鬼黄刚。 笑死捎谨,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的憔维。 我是一名探鬼主播涛救,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼业扒!你這毒婦竟也來了检吆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤程储,失蹤者是張志新(化名)和其女友劉穎蹭沛,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體章鲤,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡摊灭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了败徊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帚呼。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖集嵌,靈堂內(nèi)的尸體忽然破棺而出萝挤,到底是詐尸還是另有隱情,我是刑警寧澤根欧,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布怜珍,位于F島的核電站,受9級特大地震影響凤粗,放射性物質(zhì)發(fā)生泄漏酥泛。R本人自食惡果不足惜今豆,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柔袁。 院中可真熱鬧呆躲,春花似錦、人聲如沸捶索。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽腥例。三九已至辅甥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間燎竖,已是汗流浹背璃弄。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留构回,地道東北人夏块。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像纤掸,于是被迫代替她去往敵國和親脐供。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容