




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、1 web 端 java語言最擅長(zhǎng)的領(lǐng)域之一,也會(huì)java最廣泛應(yīng)用的地方。而高并發(fā)高吞吐量也越來越成為端普遍需求,所有能夠發(fā)出并發(fā)的應(yīng)用程序,也成為一個(gè)高級(jí)程序員的必備技能。們將從jvm 內(nèi)存模型的角度來分析虛擬機(jī)如何實(shí)現(xiàn)多線程、多線程之間由于共享和競(jìng)爭(zhēng)數(shù)據(jù)而導(dǎo)致的并發(fā)問題及解決思路。計(jì)算機(jī)硬件內(nèi)存架構(gòu)想要了解 jvm 內(nèi)存模型,們需要先了解下計(jì)算機(jī)的硬件內(nèi)存架構(gòu)正如上圖所示,經(jīng)過簡(jiǎn)化cpu與內(nèi)存操作的簡(jiǎn)易圖,實(shí)際上沒有這么簡(jiǎn)單,這里為了理解方便,們省去了南北橋并將三級(jí)緩存統(tǒng)一為cpu緩存(有些 cpu只有二級(jí)緩存,有些 cpu有三級(jí)緩存 )。就目前計(jì)算機(jī)而言,一般擁有多個(gè)cpu并且每個(gè) c
2、pu可能存在多個(gè)核心,多核指在一枚處理器(cpu)中集成兩個(gè)或多個(gè)完整的計(jì)算引擎 (內(nèi)核),這樣就可以支持多任務(wù)并行執(zhí)行,從多線程的調(diào)度來說,每個(gè)線程都會(huì)映到各個(gè) cpu核心中并行運(yùn)行。在cpu內(nèi)部有一組 cpu寄存器,寄存器cpu直接訪問和處理的數(shù)據(jù),一個(gè)臨時(shí)放數(shù)據(jù)的空間。一般cpu都會(huì)從內(nèi)存取數(shù)據(jù)到寄存器,然后進(jìn)行處理,但由于內(nèi)存的處理速度遠(yuǎn)遠(yuǎn)低于cpu,導(dǎo)致 cpu在處理指令時(shí)往往花費(fèi)很多時(shí)間在等待內(nèi)存準(zhǔn)備工作,于在寄存器和主內(nèi)存間了cpu緩存,cpu緩存比較小,但訪問速度比主內(nèi)存快得多,用它來作為內(nèi)存與處理器之間的緩沖:將運(yùn)算需要使用到的數(shù)據(jù)復(fù)制到緩存中,讓運(yùn)算能快速進(jìn)行,當(dāng)運(yùn)算結(jié)束后
3、再?gòu)木彺嫱降絻?nèi)存之中,這樣處理器就不用等待緩慢的內(nèi)存讀寫了?;诟咚倬彺娴拇鎯?chǔ)交互很好的解決了處理器與內(nèi)存的速度矛盾,但也為計(jì)算機(jī)系統(tǒng)帶來了更高的復(fù)雜度,因?yàn)樗肓艘粋€(gè)新的問題:緩存一致性。在多處理器系統(tǒng)中,每個(gè)處理器都有自己的高速緩存,而它們又共享同一主內(nèi)存(ram)。當(dāng)多個(gè)處理器的運(yùn)算任務(wù)都涉及同一塊主內(nèi)存區(qū)域時(shí),將可能導(dǎo)致各自的緩存數(shù)據(jù)不一致,2 為了解決一致性問題,需要各個(gè)處理器訪問緩存時(shí)都遵循一些協(xié)議,在讀寫時(shí)根據(jù)協(xié)議來進(jìn)行操作,這些協(xié)議有msi、mesi、mosi 等。被稱為硬件的 “ 內(nèi)存模型 ” ,可以理解為在特定的操作協(xié)議下,對(duì)特定的內(nèi)存或高速緩存進(jìn)行讀寫訪問的過程抽象。
4、不同架構(gòu)的物理機(jī)器可以擁有不一樣的內(nèi)存模型,而們的java虛擬機(jī)也有自己的內(nèi)存模型。java 線程與硬件處理器了解完硬件的內(nèi)存架構(gòu)后,接著了解jvm中線程的實(shí)現(xiàn)原理,理解線程的實(shí)現(xiàn)原理,有助于們了解java內(nèi)存模型與硬件內(nèi)存架構(gòu)的關(guān)系,在window 系統(tǒng)和linux 系統(tǒng)上, java線程的實(shí)現(xiàn)基于一對(duì)一的線程模型,所謂的一對(duì)一模型,實(shí)際上就通過語言級(jí)別層面程序去間接調(diào)用系統(tǒng)內(nèi)核的線程模型,即們?cè)谑褂胘ava線程時(shí), java虛擬機(jī)內(nèi)部轉(zhuǎn)而調(diào)用當(dāng)前操作系統(tǒng)的內(nèi)核線程來完成當(dāng)前任務(wù)。這里需要了解一個(gè)術(shù)語,內(nèi)核線程(kernel-levelthread ,klt),它由操作系統(tǒng)內(nèi)核(kernel
5、) 支持的線程,這種線程由操作系統(tǒng)內(nèi)核來完成線程切換,內(nèi)核通過操作調(diào)度器進(jìn)而對(duì)線程執(zhí)行調(diào)度,并將線程的任務(wù)映到各個(gè)處理器上。每個(gè)內(nèi)核線程可以視為內(nèi)核的一個(gè)分身 ,這也就操作系統(tǒng)可以同時(shí)處理多任務(wù)的原因。由于們編寫的多線程程序?qū)儆谡Z言層面的,程序一般不會(huì)直接去調(diào)用內(nèi)核線程,取而代之的一種輕量級(jí)的進(jìn)程 (lightweightprocess) ,也通常意義上的線程,由于每個(gè)輕量級(jí)進(jìn)程都會(huì)映到一個(gè)內(nèi)核線程,因此們可以通過輕量級(jí)進(jìn)程調(diào)用內(nèi)核線程,進(jìn)而由操作系統(tǒng)內(nèi)核將任務(wù)映到各個(gè)處理器,這種輕量級(jí)進(jìn)程與內(nèi)核線程間1 對(duì) 1 的關(guān)系就稱為一對(duì)一的線程模型。java 內(nèi)存模型內(nèi)存模型概述3 java內(nèi)存模型
6、 (即 javamemorymodel,簡(jiǎn)稱 jmm)本身一種抽象的概念,并不真實(shí)存在,它描述的一組規(guī)則或規(guī)范,通過這組規(guī)范定義了程序中各個(gè)變量(包括實(shí)例字段,靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素)的訪問。java內(nèi)存模型的主要目標(biāo)定義程序中的各個(gè)變量的訪問規(guī)則,即如何在虛擬機(jī)中將變量存儲(chǔ)到內(nèi)存和從內(nèi)存中取出。此處的變量不包括局部變量和方法參數(shù),因?yàn)樗鼈兙€程私有的,不會(huì)被共享,自然不存在競(jìng)爭(zhēng)問題。由于jvm運(yùn)行程序的實(shí)體線程,而每個(gè)線程創(chuàng)建時(shí)jvm 都會(huì)為其創(chuàng)建一個(gè)工作內(nèi)存(有些地方稱為棧空間 ),用于存儲(chǔ)線程私有的數(shù)據(jù),而java內(nèi)存模型中規(guī)定所有變量都存儲(chǔ)在主內(nèi)存,主內(nèi)存共享內(nèi)存區(qū)域,所有線程都可
7、以訪問,但線程對(duì)變量的操作(讀取賦值等 )必須在工作內(nèi)存中進(jìn)行,首先要將變量從主內(nèi)存拷貝的自己的工作內(nèi)存空間,然后對(duì)變量進(jìn)行操作,操作完成后再將變量寫回主內(nèi)存,不能直接操作主內(nèi)存中的變量,工作內(nèi)存中存儲(chǔ)著主內(nèi)存中的變量副本拷貝,前面說過,工作內(nèi)存每個(gè)線程的私有數(shù)據(jù)區(qū)域,因此不同的線程間無法訪問對(duì)方的工作內(nèi)存,線程間的通信(傳值)必須通過主內(nèi)存來完成,線程、主內(nèi)存、工作內(nèi)存三者的關(guān)系如下圖弄清楚主內(nèi)存和工作內(nèi)存后,接了解一下主內(nèi)存與工作內(nèi)存的數(shù)據(jù)存儲(chǔ)類型以及操作,根據(jù)虛擬機(jī)規(guī)范,對(duì)于一個(gè)實(shí)例對(duì)象中的成員方法而言,如果方法中包含本地變量基本數(shù)據(jù)類型( boolean,byte,short,char
8、,int,long,float,double ),將直接存儲(chǔ)在工作內(nèi)存的幀棧結(jié)構(gòu)中,但倘若本地變量引用類型,那么該變量的引用會(huì)存儲(chǔ)在功能內(nèi)存的幀棧中,而對(duì)象實(shí)例將存儲(chǔ)在主內(nèi)存(共享數(shù)據(jù)區(qū)域,堆 )中。但對(duì)于實(shí)例對(duì)象的成員變量,不管它基本數(shù)據(jù)類型或者包裝類型(integer 、double 等)還引用類型,都會(huì)被存儲(chǔ)到堆區(qū)。至于static 變量以及類本身相關(guān)信息將會(huì)存儲(chǔ)在主內(nèi)存中。需要注意的,在主內(nèi)存中的實(shí)例對(duì)象可以被多線程共享,倘若兩個(gè)線程同時(shí)調(diào)4 用了同一個(gè)對(duì)象的同一個(gè)方法,那么兩條線程會(huì)將要操作的數(shù)據(jù)拷貝一份到自己的工作內(nèi)存中,執(zhí)行完成操作后才刷新到主內(nèi)存。java 內(nèi)存模型與 java
9、 內(nèi)存區(qū)域關(guān)系這里需要注意下 java內(nèi)存模型中的主內(nèi)存、工作內(nèi)存與java內(nèi)存區(qū)域中的 java堆、棧、方法區(qū)不同一層次的內(nèi)存劃分,不要混淆。java 內(nèi)存模型 主內(nèi)存主要存儲(chǔ)的 java實(shí)例對(duì)象,所有線程創(chuàng)建的實(shí)例對(duì)象都存放在主內(nèi)存中,不管該實(shí)例對(duì)象成員變量還方法中的本地變量(也稱局部變量 ),當(dāng)然也包括了共享的類信息、常量、靜態(tài)變量。由于共享數(shù)據(jù)區(qū)域,多條線程對(duì)同一個(gè)變量進(jìn)行訪問可能會(huì)發(fā)現(xiàn)線程問題。 工作內(nèi)存主要存儲(chǔ)當(dāng)前方法的所有本地變量信息(工作內(nèi)存中存儲(chǔ)著主內(nèi)存中的變量副本拷貝),每個(gè)線程只能訪問自己的工作內(nèi)存,即線程中的本地變量對(duì)其它線程不可見的,就算兩個(gè)線程執(zhí)行的同一段代碼,它們
10、也會(huì)各自在自己的工作內(nèi)存中創(chuàng)建屬于當(dāng)前線程的本地變量,當(dāng)然也包括了字節(jié)碼行號(hào)指示器、相關(guān)native方法的信息。注意由于工作內(nèi)存每個(gè)線程的私有數(shù)據(jù),線程間無法相互訪問工作內(nèi)存,因此存儲(chǔ)在工作內(nèi)存的數(shù)據(jù)不存程問題。java 內(nèi)存區(qū)域 方法區(qū)( methodarea )5 方法區(qū)屬于線程共享的內(nèi)存區(qū)域,又稱non-heap(非堆),主要用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù),根據(jù)java虛擬機(jī)規(guī)范的規(guī)定,當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時(shí),將拋出outofmemoryerror異常。值得注意的在方法區(qū)中存在一個(gè)叫運(yùn)行時(shí)常量池(runtimeconstantpool
11、 )的區(qū)域,它主要用于存放編譯器生成的各種字面量和符號(hào)引用,這些內(nèi)容將在類加載后存放到運(yùn)行時(shí)常量池中,以便后續(xù)使用。 jvm堆(javaheap )java堆也屬于線程共享的內(nèi)存區(qū)域,它在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建,java虛擬機(jī)所管理的內(nèi)存中的一塊,主要用于存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存,注意 java堆垃圾收集器管理的主要區(qū)域,因此很多時(shí)候也被稱gc堆,如果在堆中沒有內(nèi)存完成實(shí)例分配,并且堆也無法再擴(kuò)展時(shí),將會(huì)拋出outofmemoryerror異常。 程序計(jì)數(shù)器 (programcounterregister) 屬于線程私有的數(shù)據(jù)區(qū)域,一小塊內(nèi)存空間,主要代表當(dāng)前線程所執(zhí)行的字節(jié)
12、碼行號(hào)指示器。字節(jié)碼解釋器工作時(shí),通過改變這個(gè)計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(gè)計(jì)數(shù)器來完成。 虛擬機(jī)棧 (javavirtualmachinestacks) 屬于線程私有的數(shù)據(jù)區(qū)域,與線程同時(shí)創(chuàng)建,總數(shù)與線程關(guān)聯(lián),代表java方法執(zhí)行的內(nèi)存模型。每個(gè)方法執(zhí)行時(shí)都會(huì)創(chuàng)建一個(gè)棧楨來存儲(chǔ)方法的的變量表、操作數(shù)棧、動(dòng)態(tài)方法、返回值、返回等信息。每個(gè)方法從調(diào)用直結(jié)束就對(duì)于一個(gè)棧楨在虛擬機(jī)棧中的入棧和出棧過程。 本地方法棧 (nativemethodstacks) 本地方法棧屬于線程私有的數(shù)據(jù)區(qū)域,這部分主要與虛擬機(jī)用到的native
13、 方法相關(guān),一般情況下,們無需關(guān)心此區(qū)域。java 內(nèi)存模型與硬件內(nèi)存架構(gòu)的關(guān)系通過對(duì)前面的硬件內(nèi)存架構(gòu)、java內(nèi)存模型以及 java多線程的實(shí)現(xiàn)原理的了解,們應(yīng)該已經(jīng)意識(shí)到,多線程的執(zhí)行最終都會(huì)映到硬件處理器上進(jìn)行執(zhí)行,但java內(nèi)存模型和硬件內(nèi)存架構(gòu)并不完全一致。對(duì)于硬件內(nèi)存來說只有寄存器、緩存內(nèi)存、主內(nèi)存的概念,并沒有工作內(nèi)存(線程私有數(shù)據(jù)區(qū)域 )和主內(nèi)存 (堆內(nèi)存 )之分,也就說 java內(nèi)存模型對(duì)內(nèi)存的劃分對(duì)硬件內(nèi)存并沒有任何影響,因?yàn)閖mm 只一種抽象的概念,一組規(guī)則,并不實(shí)際存在,不管工作內(nèi)存的數(shù)據(jù)還主內(nèi)存的數(shù)據(jù),對(duì)于計(jì)算機(jī)硬件來說都會(huì)存儲(chǔ)在計(jì)算機(jī)主內(nèi)存中,當(dāng)然也有可能存儲(chǔ)到c
14、pu緩存或者寄存器中,因此總體上來說,java內(nèi)存模型和計(jì)算機(jī)硬件內(nèi)存架構(gòu)一個(gè)相互交叉的關(guān)系,一種抽象概念劃分與真實(shí)物理硬件的交叉。(注意對(duì)于 java內(nèi)存區(qū)域劃分也同樣的道理 ) 6 jmm 存在的必要性在明白了 java內(nèi)存區(qū)域劃分、硬件內(nèi)存架構(gòu)、java多線程的實(shí)現(xiàn)原理與java內(nèi)存模型的具體關(guān)系后,接著來談?wù)刯ava內(nèi)存模型存在的必要性。由于jvm 運(yùn)行程序的實(shí)體線程,而每個(gè)線程創(chuàng)建時(shí)jvm 都會(huì)為其創(chuàng)建一個(gè)工作內(nèi)存(有些地方稱為??臻g),用于存儲(chǔ)線程私有的數(shù)據(jù),線程與主內(nèi)存中的變量操作必須通過工作內(nèi)存間接完成,主要過程將變量從主內(nèi)存拷貝的每個(gè)線程各自的工作內(nèi)存空間,然后對(duì)變量進(jìn)行操作
15、,操作完成后再將變量寫回主內(nèi)存,如果存在兩個(gè)線程同時(shí)對(duì)一個(gè)主內(nèi)存中的實(shí)例對(duì)象的變量進(jìn)行操作就有可能誘發(fā)線程問題。如下圖,主內(nèi)存中存在一個(gè)共享變量x,現(xiàn)在有 a 和 b 兩條線程分別對(duì)該變量x=1進(jìn)行操作, a/b 線程各自的工作內(nèi)存中存在共享變量副本x。假設(shè)現(xiàn)在 a 線程想要x 的值為 2,而 b 線程卻想要讀取x 的值,那么 b 線程讀取到的值a 線程更新后的值 2 還更新前的值 1 呢?答案,不確定,即b 線程有可能讀取到a 線程更新前的值 1,也有可能讀取到a 線程更新后的值2,這因?yàn)楣ぷ鲀?nèi)存每個(gè)線程私有的數(shù)據(jù)區(qū)域,而線程 a 變量 x 時(shí),首先將變量從主內(nèi)存拷貝到a 線程的工作內(nèi)存中,
16、然后對(duì)變量進(jìn)行操作,操作完成后再將變量x 寫回主內(nèi),而對(duì)于b 線程的也類似的,這樣就有可能造成主內(nèi)存與工作內(nèi)存間數(shù)據(jù)存在一致性問題,假如a 線程完后正在將數(shù)據(jù)寫回主內(nèi)存,而b 線程此時(shí)正在讀取主內(nèi)存,即將x=1 拷貝到自己的工作內(nèi)存中,這樣b 線程讀取到的值就 x=1,但如果 a 線程已將 x=2 寫回主內(nèi)存后, b 線程才始讀取的話,那么此時(shí)b 線程讀取到的就x=2,但到底哪種情況先發(fā)生呢?這不確定的,這也就所謂的線程問題。7 為了解決類似上述的問題,jvm定義了一組規(guī)則,通過這組規(guī)則來決定一個(gè)線程對(duì)共享變量的寫入何時(shí)對(duì)另一個(gè)線程可見,這組規(guī)則也稱為java內(nèi)存模型(即jmm),jmm 圍繞
17、著程序執(zhí)行的原子性、有序性、可見性展的,們看看這三個(gè)特性。內(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 種操作來完成,虛擬機(jī)實(shí)現(xiàn)時(shí)必須保證提及的每一種操作都原子的、不可再分的。 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)作使
18、用。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è)操作。8 store(存儲(chǔ)):作用于工作內(nèi)存的變量,它把工作內(nèi)存中一個(gè)變量的值傳送到主內(nèi)存中,以便隨后的write 操作使用。write (寫入):作用于主內(nèi)存的變量,它把store 操作從工作內(nèi)存中
19、得到的變量的值放入主內(nèi)存的變量中。如果要把一個(gè)變量從主內(nèi)存復(fù)制到工作內(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
20、和 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ì)其
21、進(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 操作)。這 8 種內(nèi)存訪問操作以及上述規(guī)則限定,再加上稍后介紹的對(duì)vol
22、atile 的一些特殊規(guī)定,就已經(jīng)完全確定了java程序中哪些內(nèi)存訪問操作在并發(fā)下的。由于這種定義相當(dāng)嚴(yán)謹(jǐn)?shù)质譄┈?,?shí)踐起來很麻煩,所以在12.3.6 節(jié)中筆者將介紹這種定義的一個(gè)等效判斷原則先行發(fā)生原則,用來確定一個(gè)訪問在并發(fā)環(huán)境下否。原子性、可見性與有序性原子性原子性指的一個(gè)操作不可中斷的,即使在多線程環(huán)境下,一個(gè)操作一旦始就不會(huì)被其他線程影響。由java內(nèi)存模型來直接保證的原子性變量操作包括read、load、assign、use、store 和 write ,們致可以認(rèn)為基本數(shù)據(jù)類型的訪問讀寫具備原子性的,但對(duì)于 64 位的數(shù)據(jù)類型( long 和 double),在模型中特別定義
23、了一條相對(duì)寬松的規(guī)定:允許虛擬機(jī)將沒有被volatile 修飾的 64 位數(shù)據(jù)的讀寫操作劃分為兩9 次 32 位的操作來進(jìn)行,這樣會(huì)導(dǎo)致一個(gè)線程在寫時(shí),操作完前32 位的原子操作后,輪到 b 線程讀取時(shí),恰好只讀取到了后32 位的數(shù)據(jù),這樣可能會(huì)讀取到一個(gè)既非原值又不線程值的變量,它可能“ 半個(gè)變量 ” 的數(shù)值,即 64 位數(shù)據(jù)被兩個(gè)線程分成了兩次讀取。但也不必太擔(dān)心,因?yàn)樽x取到“ 半個(gè)變量 ” 的情況比較少見,至少在目前的商用的虛擬機(jī)中,幾乎都把64 位的數(shù)據(jù)的讀寫操作作為原子操作來執(zhí)行,因此對(duì)于這個(gè)問題不必太在意,知道這么回事即可。如果應(yīng)用場(chǎng)景需要一個(gè)更范圍的原子性保證(經(jīng)常會(huì)遇到),ja
24、va內(nèi)存模型還了lock 和 unlock 操作來滿足這種需求,盡管虛擬機(jī)未把lock 和 unlock 操作直接放給用戶使用,但卻了更高層次的字節(jié)碼指令monitorenter和 monitorexit來隱式地使用這兩個(gè)操作,這兩個(gè)字節(jié)碼指令反映到j(luò)ava代碼中就同步塊 synchronized 關(guān)鍵字,因此在 synchronized 塊之間的操作也具備原子性。可見性可見性指當(dāng)一個(gè)線程了共享變量的值,其他線程能夠立即得知這個(gè)。對(duì)于串行程序來說,可見性問題不存在的,因?yàn)閭冊(cè)谌魏我粋€(gè)操作中了某個(gè)變量的值,后續(xù)的操作中都能讀取這個(gè)變量值,并且過的新值。但在多線程環(huán)境中可就不一定了,前面?zhèn)兎治鲞^,由于線程對(duì)共享變量的操作都線程拷貝到各自的工作內(nèi)存進(jìn)行操作后才寫回到主內(nèi)存中的,這就可能存在一個(gè)
溫馨提示
- 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. 人人文庫(kù)網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 人教版部編版小學(xué)語文一年級(jí)上冊(cè)青蛙寫詩(shī)教學(xué)設(shè)計(jì)教案11
- 2025年全民健康知識(shí)競(jìng)賽試題
- 總賬會(huì)計(jì)年度工作總結(jié)
- 羊水栓塞的護(hù)理健康指導(dǎo)
- 防拋網(wǎng)施工方案
- 多聯(lián)機(jī)施工方案
- 砂石回填施工方案
- 雨棚的施工方案
- 2025年計(jì)算機(jī)系統(tǒng)配套用各種消耗品項(xiàng)目合作計(jì)劃書
- 卒中預(yù)防3分鐘科普
- 《汽車電子電氣系統(tǒng)構(gòu)造與拆裝》課件 項(xiàng)目三 起動(dòng)系統(tǒng)檢修
- 《安徒生童話》閱讀指導(dǎo)課件
- 沉淀滴定法(應(yīng)用化學(xué)課件)
- 室外道路及管網(wǎng)工程擬投入的主要施工機(jī)械設(shè)備及測(cè)量?jī)x器表
- 07K506 多聯(lián)式空調(diào)機(jī)系統(tǒng)設(shè)計(jì)與施工安裝
- 腹部外傷護(hù)理查房記錄
- 橋面鋪裝三維激光攤鋪施工工法
- 優(yōu)質(zhì)課一等獎(jiǎng)小學(xué)綜合實(shí)踐《我也能發(fā)明》課件
- 部編人教版三年級(jí)下冊(cè)語文:荷花課件
- 螺紋牙強(qiáng)度校核計(jì)算
- 關(guān)于在生產(chǎn)過程中物料流轉(zhuǎn)的交接和管理規(guī)定
評(píng)論
0/150
提交評(píng)論