一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

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

 一本正經(jīng)地胡鬧 2019-12-03

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

1.1 線程之間的通信

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

1.2 線程間的同步

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

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

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

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

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

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

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

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

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

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

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

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

    image.png

所有原始類型(boolean,byte,short,char,int,long,float,double)的局部變量都直接保存在線程棧當(dāng)中,對(duì)于它們的值各個(gè)線程之間都是獨(dú)立的。對(duì)于原始類型的局部變量,一個(gè)線程可以傳遞一個(gè)副本給另一個(gè)線程,當(dāng)它們之間是無法共享的。
堆區(qū)包含了Java應(yīng)用創(chuàng)建的所有對(duì)象信息,不管對(duì)象是哪個(gè)線程創(chuàng)建的,其中的對(duì)象包括原始類型的封裝類(如Byte、Integer、Long等等)。不管對(duì)象是屬于一個(gè)成員變量還是方法中的局部變量,它都會(huì)被存儲(chǔ)在堆區(qū)。
一個(gè)局部變量如果是原始類型,那么它會(huì)被完全存儲(chǔ)到棧區(qū)。 一個(gè)局部變量也有可能是一個(gè)對(duì)象的引用,這種情況下,這個(gè)本地引用會(huì)被存儲(chǔ)到棧中,但是對(duì)象本身仍然存儲(chǔ)在堆區(qū)。
對(duì)于一個(gè)對(duì)象的成員方法,這些方法中包含局部變量,仍需要存儲(chǔ)在棧區(qū),即使它們所屬的對(duì)象在堆區(qū)。 對(duì)于一個(gè)對(duì)象的成員變量,不管它是原始類型還是包裝類型,都會(huì)被存儲(chǔ)到堆區(qū)。Static類型的變量以及類本身相關(guān)信息都會(huì)隨著類本身存儲(chǔ)在堆區(qū)。

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

2.3.1 可見性問題

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

2.3.2 競(jìng)爭(zhēng)現(xiàn)象

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

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

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

2.4.1 重排序類型

  • 1)編譯器優(yōu)化的重排序。編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執(zhí)行順序。

  • 2)指令級(jí)并行的重排序?,F(xiàn)代處理器采用了指令級(jí)并行技術(shù)(Instruction-LevelParallelism,ILP)來將多條指令重疊執(zhí)行。如果不存在數(shù)據(jù)依賴性,處理器可以改變語句對(duì)應(yīng)機(jī)器指令的執(zhí)行順序。

  • 3)內(nèi)存系統(tǒng)的重排序。由于處理器使用緩存和讀/寫緩沖區(qū),這使得加載和存儲(chǔ)操作看上去可能是在亂序執(zhí)行。

2.4.2 重排序與依賴性

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

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

  • as-if-serial
    不管如何重排序,都必須保證代碼在單線程下的運(yùn)行正確,連單線程下都無法正確,更不用討論多線程并發(fā)的情況,所以就提出了一個(gè)as-if-serial的概念。
    as-if-serial語義的意思是:不管怎么重排序(編譯器和處理器為了提高并行度),(單線程)程序的執(zhí)行結(jié)果不能被改變。編譯器、runtime和處理器都必須遵守as-if-serial語義。為了遵守as-if-serial語義,編譯器和處理器不會(huì)對(duì)存在數(shù)據(jù)依賴關(guān)系的操作做重排序,因?yàn)檫@種重排序會(huì)改變執(zhí)行結(jié)果。(強(qiáng)調(diào)一下,這里所說的數(shù)據(jù)依賴性僅針對(duì)單個(gè)處理器中執(zhí)行的指令序列和單個(gè)線程中執(zhí)行的操作,不同處理器之間和不同線程之間的數(shù)據(jù)依賴性不被編譯器和處理器考慮。)但是,如果操作之間不存在數(shù)據(jù)依賴關(guān)系,這些操作依然可能被編譯器和處理器重排序。

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

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

這里假設(shè)有兩個(gè)線程A和B,A首先執(zhí)行init ()方法,隨后B線程接著執(zhí)行use ()方法。線程B在執(zhí)行操作4時(shí),能否看到線程A在操作1對(duì)共享變量a的寫入呢?答案是:不一定能看到。
由于操作1和操作2沒有數(shù)據(jù)依賴關(guān)系,編譯器和處理器可以對(duì)這兩個(gè)操作重排序;同樣,操作3和操作4沒有數(shù)據(jù)依賴關(guān)系,編譯器和處理器也可以對(duì)這兩個(gè)操作重排序。讓我們先來看看,當(dāng)操作1和操作2重排序時(shí),可能會(huì)產(chǎn)生什么效果?操作1和操作2做了重排序。程序執(zhí)行時(shí),線程A首先寫標(biāo)記變量flag,隨后線程B讀這個(gè)變量。由于條件判斷為真,線程B將讀取變量a。此時(shí),變量a還沒有被線程A寫入,這時(shí)就會(huì)發(fā)生錯(cuò)誤!
當(dāng)操作3和操作4重排序時(shí)會(huì)產(chǎn)生什么效果?
在程序中,操作3和操作4存在控制依賴關(guān)系。當(dāng)代碼中存在控制依賴性時(shí),會(huì)影響指令序列執(zhí)行的并行度。為此,編譯器和處理器會(huì)采用猜測(cè)(Speculation)執(zhí)行來克服控制相關(guān)性對(duì)并行度的影響。以處理器的猜測(cè)執(zhí)行為例,執(zhí)行線程B的處理器可以提前讀取并計(jì)算a*a,然后把計(jì)算結(jié)果臨時(shí)保存到一個(gè)名為重排序緩沖(Reorder Buffer,ROB)的硬件緩存中。當(dāng)操作3的條件判斷為真時(shí),就把該計(jì)算結(jié)果寫入變量i中。猜測(cè)執(zhí)行實(shí)質(zhì)上對(duì)操作3和4做了重排序,問題在于這時(shí)候,a的值還沒被線程A賦值。在單線程程序中,對(duì)存在控制依賴的操作重排序,不會(huì)改變執(zhí)行結(jié)果(這也是as-if-serial語義允許對(duì)存在控制依賴的操作做重排序的原因);但在多線程程序中,對(duì)存在控制依賴的操作重排序,可能會(huì)改變程序的執(zhí)行結(jié)果。

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

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

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

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

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

臨界區(qū)內(nèi)的代碼可以重排序(但JMM不允許臨界區(qū)內(nèi)的代碼“逸出”到臨界區(qū)之外,那樣會(huì)破壞監(jiān)視器的語義)。JMM會(huì)在退出臨界區(qū)和進(jìn)入臨界區(qū)這兩個(gè)關(guān)鍵時(shí)間點(diǎ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中,如果一個(gè)操作執(zhí)行的結(jié)果需要對(duì)另一個(gè)操作可見,那么這兩個(gè)操作之間必須要存在happens-before關(guān)系 。

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

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

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

在Java 規(guī)范提案中為讓大家理解內(nèi)存可見性的這個(gè)概念,提出了happens-before的概念來闡述操作之間的內(nèi)存可見性。對(duì)應(yīng)Java程序員來說,理解happens-before是理解JMM的關(guān)鍵。JMM這么做的原因是:程序員對(duì)于這兩個(gè)操作是否真的被重排序并不關(guān)心,程序員關(guān)心的是程序執(zhí)行時(shí)的語義不能被改變(即執(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ī)則:一個(gè)線程中的每個(gè)操作,happens-before于該線程中的任意后續(xù)操作。
    2)監(jiān)視器鎖規(guī)則:對(duì)一個(gè)鎖的解鎖,happens-before于隨后對(duì)這個(gè)鎖的加鎖。
    3)volatile變量規(guī)則:對(duì)一個(gè)volatile域的寫,happens-before于任意后續(xù)對(duì)這個(gè)volatile域的讀。
    4)傳遞性:如果A happens-before B,且B happens-before C,那么A happens-before C。
    5)start()規(guī)則:如果線程A執(zhí)行操作ThreadB.start()(啟動(dòng)線程B),那么A線程的ThreadB.start()操作happens-before于線程B中的任意操作。
    6)join()規(guī)則:如果線程A執(zhí)行操作ThreadB.join()并成功返回,那么線程B中的任意操作happens-before于線程A從ThreadB.join()操作成功返回。
    7 )線程中斷規(guī)則:對(duì)線程interrupt方法的調(diào)用happens-before于被中斷線程的代碼檢測(cè)到中斷事件的發(fā)生。

3.實(shí)現(xiàn)原理

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

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

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

  • 可見性。對(duì)一個(gè)volatile變量的讀,總是能看到(任意線程)對(duì)這個(gè)volatile變量最后的寫入。

  • 原子性:對(duì)任意單個(gè)volatile變量的讀/寫具有原子性,但類似于volatile++這種復(fù)合操作不具有原子性。

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

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

volatile重排序規(guī)則:

volatile內(nèi)存語義的實(shí)現(xiàn)——JMM對(duì)volatile的內(nèi)存屏障插入策略:
在每個(gè)volatile寫操作的前面插入一個(gè)StoreStore屏障。在每個(gè)volatile寫操作的后面插入一個(gè)StoreLoad屏障。
在每個(gè)volatile讀操作的后面插入一個(gè)LoadLoad屏障。在每個(gè)volatile讀操作的后面插入一個(gè)LoadStore屏障。

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

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

  • 將當(dāng)前處理器緩存行的數(shù)據(jù)寫回到系統(tǒng)內(nèi)存

  • 這個(gè)寫回內(nèi)存的操作會(huì)使在其他CPU里緩存了該內(nèi)存地址的數(shù)據(jù)無效。

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

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

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

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

  • monitorenter指令是在編譯后插入到同步代碼塊的開始位置,而monitorexit是插入到方法結(jié)束處和異常處

  • 每個(gè)monitorenter必須有對(duì)應(yīng)的monitorexit與之配對(duì)

  • 任何對(duì)象都有一個(gè)monitor與之關(guān)聯(lián),當(dāng)且一個(gè)monitor被持有后,它將處于鎖定狀態(tài)

鎖的存放位置:

3.2.2 了解各種鎖

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

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

輕量級(jí)鎖:無競(jìng)爭(zhēng)時(shí)通過CAS操作來加鎖和解鎖。(自旋鎖——是一種鎖的機(jī)制,不是狀態(tài))

重量級(jí)鎖:真正的加鎖操作

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

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

  • 在構(gòu)造函數(shù)內(nèi)對(duì)一個(gè)final域的寫入,與隨后把這個(gè)被構(gòu)造對(duì)象的引用賦值給一個(gè)引用變量,這兩個(gè)操作之間不能重排序。

  • 初次讀一個(gè)包含final域的對(duì)象的引用,與隨后初次讀這個(gè)final域,這兩個(gè)操作之間不能重排序。

final域?yàn)橐妙愋停?/p>

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

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

  • 會(huì)要求編譯器在final域的寫之后,構(gòu)造函數(shù)return之前插入一個(gè)StoreStore障屏。

  • 讀final域的重排序規(guī)則要求編譯器在讀final域的操作前面插入一個(gè)LoadLoad屏障

參考

  • 1)享學(xué)課堂Mark老師筆記

作者:王偵

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    中日韩美一级特黄大片| 国产精品日韩精品一区| 国产精品香蕉一级免费| 亚洲欧美国产中文色妇| 午夜视频成人在线观看| 亚洲国产av国产av| 午夜视频在线观看日韩| 亚洲综合精品天堂夜夜| 国产精品久久熟女吞精| 91国内视频一区二区三区| 粗暴蹂躏中文一区二区三区| 中文字幕中文字幕一区二区| 国产真人无遮挡免费视频一区| 亚洲精品中文字幕欧美| 在线观看视频成人午夜| 欧美成人精品国产成人综合| 97人妻精品一区二区三区男同| 加勒比人妻精品一区二区| 日韩欧美国产亚洲一区| 东京热男人的天堂久久综合| 亚洲av秘片一区二区三区| 九九热在线视频观看最新| 欧美日韩综合在线精品| 日本成人中文字幕一区| 日本91在线观看视频| 中文字幕熟女人妻视频| 热久久这里只有精品视频| 小黄片大全欧美一区二区| 国内外激情免费在线视频| 草草草草在线观看视频| 精品欧美国产一二三区| 中日韩免费一区二区三区| 伊人天堂午夜精品草草网| 深夜视频在线观看免费你懂| 亚洲中文字幕免费人妻| 国产成人精品99在线观看| 精品al亚洲麻豆一区| 一二区中文字幕在线观看| 日本二区三区在线播放| 在线免费看国产精品黄片| 欧美日韩中国性生活视频|