目錄
一、Java 內(nèi)存模型的主要目標(biāo)
二酝润、主內(nèi)存和工作內(nèi)存
三梳虽、內(nèi)存件的交互操作
四、對(duì)于 volatile 型變量的特殊規(guī)則
五彰居、對(duì)于 long 和 double 型變量的特殊規(guī)則
六诚纸、原子性、可見(jiàn)性與有序性
七陈惰、先行發(fā)生原則
一畦徘、Java 內(nèi)存模型的主要目標(biāo)
Java 內(nèi)存模型的主要目標(biāo)是 定義程序中各個(gè)變量的訪(fǎng)問(wèn)規(guī)則,即在虛擬機(jī)中 將變量存儲(chǔ)到內(nèi)存 和 從內(nèi)存中取出變量 這樣的底層細(xì)節(jié)抬闯。此處的變量(Variables)與 Java 變成中所說(shuō)的變量有所區(qū)別井辆,它包括了實(shí)例變量、靜態(tài)變量和構(gòu)成數(shù)組對(duì)象的元素溶握,但不包括局部變量與方法參數(shù)杯缺,因?yàn)楹笳呤蔷€(xiàn)程私有的,不會(huì)被共享睡榆,自然就不會(huì)存在競(jìng)爭(zhēng)問(wèn)題萍肆。
為了獲得較好的執(zhí)行效能,Java 內(nèi)存模型并沒(méi)有限制執(zhí)行引擎使用處理器的特定寄存器或緩存來(lái)和主內(nèi)存進(jìn)行交互胀屿,也沒(méi)有限制即時(shí)編譯器進(jìn)行調(diào)整代碼執(zhí)行順序這類(lèi)優(yōu)化措施塘揣。
二、主內(nèi)存與工作內(nèi)存
不同的線(xiàn)程之間也無(wú)法直接訪(fǎng)問(wèn)對(duì)方工作內(nèi)存中的變量房资,線(xiàn)程間變量值的傳遞均需要通過(guò)主內(nèi)存來(lái)完成,線(xiàn)程檀头、主內(nèi)存轰异、工作內(nèi)存三者的交互關(guān)系如下圖所示。Java 內(nèi)存模型規(guī)定了所有的變量都存儲(chǔ)在主內(nèi)存(Main Memory碉纳, 這里指的是虛擬機(jī)內(nèi)存的一部分)中勿负。每條線(xiàn)程還有自己的工作內(nèi)存(Working Memory,可以與處理器高速緩存類(lèi)比),線(xiàn)程的工作內(nèi)存中保存了被該線(xiàn)程使用到的變量的主內(nèi)存副本拷貝奴愉,線(xiàn)程對(duì)變量的所有操作(讀取琅摩、賦值等)都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫(xiě)主內(nèi)存中的變量锭硼。
這里說(shuō)的主內(nèi)存、工作內(nèi)存和 Java 內(nèi)存區(qū)域中的 Java 堆廊镜、棧牙肝、方法區(qū)等并不是同一個(gè)層次的內(nèi)存劃分,這兩者基本是沒(méi)有關(guān)系的嗤朴,如果兩者一定要勉強(qiáng)對(duì)應(yīng)起來(lái)的話(huà)配椭,那從變量、主內(nèi)存雹姊、工作內(nèi)存的定義來(lái)看股缸,主內(nèi)存主要對(duì)應(yīng)于 Java 堆中的對(duì)象實(shí)例數(shù)據(jù)部分,而工作內(nèi)存則對(duì)應(yīng)于 Java 虛擬機(jī)棧中的部分區(qū)域吱雏。
三敦姻、內(nèi)存件交互操作
關(guān)于 主內(nèi)存 和 工作內(nèi)存 之間具體的交互協(xié)議,即一個(gè)變量如何從主內(nèi)存拷貝到工作內(nèi)存歧杏、如何從工作內(nèi)存同步回主內(nèi)存之類(lèi)的實(shí)現(xiàn)細(xì)節(jié)替劈,Java 內(nèi)存模型中定義了一下 8 中操作來(lái)完成,虛擬機(jī)實(shí)現(xiàn)時(shí)必須保證下面提及的每一種操作都是原子的得滤、不可再分的(對(duì)于 double 和 long 類(lèi)型的變量來(lái)說(shuō),在某些平臺(tái)上允許有例外盒犹,這個(gè)問(wèn)題將在下文中說(shuō)明)懂更。
- lock(鎖定):作用于主內(nèi)存的變量,它把一個(gè)變量標(biāo)識(shí)為一條線(xiàn)程獨(dú)占的狀態(tài)急膀。
- unlock(解鎖):作用于主內(nèi)存的變量沮协,它把一個(gè)處于鎖定狀態(tài)的變量釋放出來(lái),釋放后變量才可以被其他線(xiàn)程鎖定卓嫂。
- 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í)行引擎(一般是基于操作數(shù)棧的執(zhí)行引擎)突照,每當(dāng)虛擬機(jī)遇到一個(gè)需要使用到該變量的值的 字節(jié)碼指令時(shí)將會(huì)執(zhí)行這個(gè)操作。
- assign(賦值):作用于工作內(nèi)存的變量氧吐,它把從執(zhí)行引擎接收到的值賦值給工作內(nèi)存的變量(存放在局部變量表中)讹蘑,每當(dāng)虛擬機(jī)遇到一個(gè)給變量賦值的字節(jié)碼指令時(shí)執(zhí)行這個(gè)操作。
- store(存儲(chǔ)):作用于工作內(nèi)存的變量筑舅,它把工作內(nèi)存中的變量傳送到主內(nèi)存中座慰,以便隨后的 write 操作使用。
- write(寫(xiě)入):作用于主內(nèi)存的變量翠拣,它把 store 操作從工作內(nèi)存中得到的變量的值方法主內(nèi)存的變量中版仔。
Java 內(nèi)存模型規(guī)定了在執(zhí)行上述 8 中基本操作時(shí)必須滿(mǎn)足如下規(guī)則:
- 不允許 read 和 load蝉揍、store 和 write 操作之一單獨(dú)出現(xiàn),即不允許一個(gè)變量從主內(nèi)存讀取了但工作內(nèi)存不接受畦娄,或者工作內(nèi)存發(fā)起會(huì)回寫(xiě)了但主內(nèi)存不接受的情況出現(xiàn)又沾。
- 不允許一個(gè)現(xiàn)場(chǎng)丟棄它的最近的 assign 操作,即變量的值在工作內(nèi)存改變了以后必須把該變化同步到主內(nèi)存中熙卡。
- 不允許一個(gè)線(xiàn)程無(wú)原因的(沒(méi)有發(fā)生過(guò)任何 assing 操作)把數(shù)據(jù)從線(xiàn)程的工作內(nèi)存同步回主內(nèi)存中杖刷。
- 一個(gè)新的變量只能在主內(nèi)存中“誕生”,不允許在工作內(nèi)存使用一個(gè)未被初始化(load 或 assing)的變量驳癌,換句話(huà)說(shuō)滑燃,就是對(duì)一個(gè)變量實(shí)施 use、store 操作之前颓鲜,必須先執(zhí)行過(guò) assign 和 load 操作表窘。
- 一個(gè)變量在同一個(gè)時(shí)刻只允許一條線(xiàn)程對(duì)其進(jìn)行 lock 操作,但 lock 操作可以被同一線(xiàn)程重復(fù)執(zhí)行多次甜滨,多次執(zhí)行 lock 之后乐严,只有執(zhí)行相同次的 unlock 操作,變量才會(huì)被解鎖衣摩。
- 如果對(duì)一個(gè)變量進(jìn)行 lock 操作昂验,那么將會(huì)清空工作內(nèi)存中這個(gè)變量的值,在執(zhí)行引擎使用這個(gè)變量前,需要重新執(zhí)行 load 或 assign 操作初始化變量的值既琴。
- 如果一個(gè)變量事先沒(méi)有被 lock 操作鎖定占婉,那就不允許對(duì)它執(zhí)行 unlock 操作,也不允許去 unlock 一個(gè)被其他線(xiàn)程鎖定住的變量呛梆。
- 對(duì)一個(gè)變量執(zhí)行 unlock 之前锐涯,必須先把此變量同步回主內(nèi)存中(執(zhí)行 store 以及 write 操作)。
這 8 中內(nèi)存方法操作以及上述規(guī)則限定填物,再加上稍后 volatile 的一些特殊規(guī)定纹腌,就已經(jīng)完全確定了 Java 程序中哪些內(nèi)存訪(fǎng)問(wèn)操作在并發(fā)下是安全的。
四滞磺、對(duì)于 volatile 型變量的特殊規(guī)則
當(dāng)一個(gè)變量定義為 volatile 之后升薯,它將具備兩種特性:保證變量的可見(jiàn)性 和 禁止指令重排序優(yōu)化。
-
第一個(gè)是保證此變量對(duì)所有線(xiàn)程的可見(jiàn)性击困,這里的“可見(jiàn)性”是指當(dāng)一條線(xiàn)程改變了這個(gè)變量的值涎劈,新值對(duì)于其他線(xiàn)程來(lái)說(shuō)是 立即得知 的。而普通變量做不到這一點(diǎn)阅茶,普通變量的值在線(xiàn)程間傳遞均需要通過(guò)主內(nèi)存來(lái)完成蛛枚,例如,線(xiàn)程 A 修改了一個(gè)普通變量的值脸哀,然后向主內(nèi)存進(jìn)行回寫(xiě)蹦浦,另外一個(gè)線(xiàn)程 B 在線(xiàn)程 A 回寫(xiě)完成了之后再?gòu)闹鲀?nèi)存進(jìn)行讀取操作,新變量才會(huì)對(duì)線(xiàn)程 B 可見(jiàn)撞蜂。
關(guān)于 volatile 變量的可見(jiàn)性盲镶,寫(xiě)操作在并發(fā)下是安全的,但是 volatile 變量的運(yùn)算在并發(fā)下不是安全 的蝌诡,因?yàn)?Java 里面的運(yùn)算并非原子操作溉贿。 - 使用 volatile 變量的第二個(gè)語(yǔ)義是禁止指令重排序優(yōu)化,普通的變量?jī)H僅會(huì)保證在該方法的執(zhí)行過(guò)程中所有依賴(lài)賦值結(jié)果的地方都能獲取到正確的結(jié)果浦旱,而不能保證變量賦值操作的順序與程序代碼中的執(zhí)行順序一致宇色。
了解了volatile 的語(yǔ)義問(wèn)題,那么我們來(lái)比較一下 使用 volatile 的代碼和使用其他的同步工具更快哪個(gè)更快颁湖?
- 在某些情況下代兵,volatile 的同步機(jī)制的性能確實(shí)要優(yōu)于(使用 synchronized 關(guān)鍵字或 java.util.concurrent 包里面的鎖),但是由于虛擬機(jī)對(duì)鎖實(shí)行的許多消除和優(yōu)化爷狈,使得我們很難量化地認(rèn)為 volatile 就會(huì)比 synchronized 塊多少。如果讓 volatile 自己與普通變量比較:volatile 變量讀操作的性能消耗與普通變量幾乎沒(méi)有什么差別裳擎,但是寫(xiě)操作則可能會(huì)慢一些涎永,因?yàn)樗枰诒镜卮a中插入許多內(nèi)存屏障指令來(lái)保證處理器不發(fā)生亂序執(zhí)行。
最后看一下 Java 內(nèi)存模型對(duì) volatile 變量定義的特殊規(guī)則。假定 T 表示一個(gè)線(xiàn)程羡微, V 和 W 分別表示兩個(gè) volatile 型變量谷饿,那么在進(jìn)行 read、load妈倔、use博投、assign、store 和 write 操作時(shí)需要滿(mǎn)足如下規(guī)則:
- 只有當(dāng)線(xiàn)程 T 對(duì)變量 V 執(zhí)行的前一個(gè)動(dòng)作是 load 的時(shí)候盯蝴,線(xiàn)程 T 才能對(duì)變量 V 執(zhí)行 use 動(dòng)作毅哗;并且,只有當(dāng)線(xiàn)程 T 對(duì)變量 V 執(zhí)行的后一個(gè)動(dòng)作是 use 的時(shí)候捧挺,線(xiàn)程 T 才能對(duì)變量 V 執(zhí)行 load 動(dòng)作虑绵。線(xiàn)程 T 對(duì)變量 V 的 use 動(dòng)作可以認(rèn)為是和線(xiàn)程 T 對(duì)變量 V 的 load、read 動(dòng)作相關(guān)聯(lián)闽烙,必須連續(xù)一起出現(xiàn)(這條規(guī)則要求在工作內(nèi)存中翅睛,每次使用 V 前都必須先沖主內(nèi)存刷新最新的值,用于保證能看見(jiàn)其他線(xiàn)程對(duì)變量 V 所做的修改后的值)黑竞。
- 只有當(dāng)線(xiàn)程 T 對(duì)變量 V 執(zhí)行的前一個(gè)動(dòng)作是 assign 的時(shí)候捕发,線(xiàn)程 T 才能對(duì)變量 V 執(zhí)行 store 動(dòng)作;并且很魂,只有當(dāng)線(xiàn)程 T 對(duì)變量 V 執(zhí)行的后一個(gè)動(dòng)作是 store 的時(shí)候扎酷,線(xiàn)程 T 才能對(duì)變量 V 執(zhí)行 assign 動(dòng)作。線(xiàn)程 T 對(duì)變量 V 的 assign 動(dòng)作可以認(rèn)為是和線(xiàn)程 T 對(duì)變量 V 的 store莫换、write 動(dòng)作相關(guān)聯(lián)霞玄,必須連續(xù)一起出現(xiàn)(這條規(guī)則要求在工作內(nèi)存中,每次修改 V 后都必須立刻同步回主內(nèi)存中拉岁,用于保證其他線(xiàn)程可以看到自己對(duì)變量 V 所做的修改)坷剧。
- 假定動(dòng)作 A 是線(xiàn)程 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)作惫企;類(lèi)似的,假定動(dòng)作 B 是線(xiàn)程 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(這條規(guī)則要求 volatile 修飾的變量不會(huì)被指令重排序優(yōu)化偏序,保證代碼的執(zhí)行順序與程序相同)。
五胖替、對(duì)于 long 和 double 型變量的特殊規(guī)則
Java 內(nèi)存模型要求 lock研儒、unlock豫缨、read、load端朵、use好芭、assign、store冲呢、write 這 8 個(gè)操作都具有原子性舍败,但是對(duì)于 64 位的數(shù)據(jù)類(lèi)型(long 和 double),在模型中特別定義了一條相對(duì)寬松的規(guī)定:允許虛擬機(jī)將沒(méi)有被 volatile 修飾的 64 位數(shù)據(jù)的讀寫(xiě)操作劃分為兩次 32 位的操作來(lái)進(jìn)行敬拓,即允許虛擬機(jī)實(shí)現(xiàn)選擇可以不保證 64 位數(shù)據(jù)類(lèi)型的 load邻薯、store、read 和 write 這 4 個(gè)操作的原子性恩尾。
不過(guò)弛说,在實(shí)際開(kāi)發(fā)中,目前各種平臺(tái)下的商用虛擬機(jī)幾乎都選擇把 64 位的數(shù)據(jù)的讀寫(xiě)操作作為院子操作來(lái)對(duì)待翰意,因此我們?cè)诰帉?xiě)代碼時(shí)一般不需要把用到的 long 和 double 變量專(zhuān)門(mén)聲明為 volatile木人。
六、原子性冀偶、可見(jiàn)性與有序性
Java 內(nèi)存模型是圍繞著在并發(fā)過(guò)程中如何處理原子性醒第、可見(jiàn)性和有序性這 3 個(gè)特征來(lái)建立的。
-
原子性(Atomicity):由 Java 內(nèi)存模型來(lái)直接保證的原子性變量操作包括 read进鸠、load稠曼、use、assign客年、store 和 write霞幅,我們大致可以認(rèn)為 基本數(shù)據(jù)類(lèi)型 的訪(fǎng)問(wèn)讀寫(xiě)是具備原子性的(例外就是 long 和 double 的非原子協(xié)定)。
如果應(yīng)用場(chǎng)景需要一個(gè)更大范圍(包括基本數(shù)據(jù)類(lèi)型以外的變量)的原子性保證量瓜,Java 內(nèi)存模型還提供了 lock 和 unlock 操作來(lái)滿(mǎn)足這種需求司恳,盡管虛擬機(jī)未把 lock 和 unlock 操作直接開(kāi)發(fā)給用戶(hù)使用,但是卻提供了更高層次的字節(jié)碼指令 monitorenter 和 monitorexit 來(lái)隱式地使用這兩個(gè)操作绍傲,這兩字節(jié)碼指令反映到 Java 代碼中就是同步塊——synchronized 關(guān)鍵字扔傅,因此在 synchronized 塊之間的操作也具備原子性。
也就是說(shuō) Java 內(nèi)存模型中的 read烫饼、load猎塞、use、assign杠纵、store 和 wriet 這 6 種操作可以保證 基本數(shù)據(jù)類(lèi)型 的原子性荠耽,而 Java 內(nèi)存模型定義的 lock 和 unlock 操作則可以保證 基本數(shù)據(jù)類(lèi)型 以外的數(shù)據(jù)類(lèi)型的原子性。 -
可見(jiàn)性(Visibility):可見(jiàn)性是指一個(gè)線(xiàn)程修改了一個(gè)變量的值比藻,其他線(xiàn)程可以立即得知這個(gè)修改铝量。Java 內(nèi)存模型是通過(guò)在工作內(nèi)存中變量修改后將新值立即同步回主內(nèi)存伊履,在工作內(nèi)存中變量讀取前從主內(nèi)存刷新新變量的值這種依賴(lài)主內(nèi)存作為傳遞媒介的方式來(lái)實(shí)現(xiàn)可見(jiàn)性的。無(wú)論是普通變量還是 volatile 變量都是如此款违,普通變量與 volatile 變量的區(qū)別是,volatile 的特殊規(guī)則保證了新值能立即同步到主內(nèi)存群凶,以及每次使用前立即從主內(nèi)存刷新插爹。因此,可以說(shuō) volatile 保證了多線(xiàn)程操作時(shí)變量的可見(jiàn)性请梢,而普通變量則不能保證這一點(diǎn)赠尾。
除了 volatile 之外,Java 還有兩個(gè)關(guān)鍵字能實(shí)現(xiàn)可見(jiàn)性毅弧,即 synchronized 和 final气嫁。同步塊的可見(jiàn)性是由“對(duì)一個(gè)變量執(zhí)行 unlock 操作之前,必須先把工作內(nèi)存中此變量的值同步回主內(nèi)存中(執(zhí)行 store够坐、write 操作)”這條規(guī)則獲得的寸宵,而 final關(guān)鍵字的可見(jiàn)性是指:被 final 修飾的字段在構(gòu)造器中一旦初始化完成,并且構(gòu)造器沒(méi)有把 “this” 引用傳遞出去元咙,那在其他線(xiàn)程中就能看見(jiàn) final 字段的值梯影。 -
有序性(Ordering):Java 程序中天然的有序性可以總結(jié)為一句話(huà):如果在本線(xiàn)程內(nèi)觀察,所有的操作都是有序的庶香;如果在一個(gè)線(xiàn)程中觀察另一個(gè)線(xiàn)程甲棍,所有的操作都是無(wú)序的。前半句是指“線(xiàn)程內(nèi)表現(xiàn)為串行的語(yǔ)義”赶掖,后半句是指“指令重排序”現(xiàn)象和“工作內(nèi)存與主內(nèi)存同步延遲”現(xiàn)象感猛。
Java 語(yǔ)言提供了 volatile 和 synchronized 兩個(gè)關(guān)鍵字來(lái)保證線(xiàn)程之間操作的有序性,volatile 關(guān)鍵字本身就包含了進(jìn)制指令重排序的語(yǔ)義奢赂,而 synchronized 則是由“一個(gè)變量在同一個(gè)時(shí)刻只允許一條線(xiàn)程對(duì)其進(jìn)行 lock 操作”這條規(guī)則獲得的陪白,這條規(guī)則決定了持有同一個(gè)鎖的兩個(gè)同步塊只能串行的進(jìn)入。
介紹完并發(fā)中 3 中重要的特性后呈驶,有沒(méi)有發(fā)現(xiàn) synchronized 關(guān)鍵字在需要 3 中特性的時(shí)候都可以作為其中一種的解決方案拷泽。
七、先行發(fā)生原則
如果 Java 內(nèi)存模型中所有的有序性都僅僅靠 volatile 和 synchronized 來(lái)完成袖瞻,那么有一些操作將會(huì)變得很煩瑣司致,但是我們?cè)诰帉?xiě) Java 并發(fā)代碼的時(shí)候并沒(méi)有感覺(jué)到這一點(diǎn),這是因?yàn)?Java 語(yǔ)言中有一個(gè) “先行發(fā)生”(happens-before)的原則聋迎。這個(gè)原則非常重要脂矫,它是判斷數(shù)據(jù)是否存在競(jìng)爭(zhēng)、線(xiàn)程是否安全的主要依據(jù)霉晕,依靠這個(gè)原則庭再,我們可以通過(guò)幾條規(guī)則一攬子地解決并發(fā)環(huán)境下兩個(gè)操作之間是否可能存在沖突的所有問(wèn)題捞奕。
下面是 Java 內(nèi)存模型下一些“天然的”先行發(fā)生關(guān)系,這些先行發(fā)生關(guān)系無(wú)須任何同步器協(xié)助就已經(jīng)存在拄轻,可以在編碼 中直接使用颅围。如果兩個(gè)操作之間的關(guān)系不在此列,并且無(wú)法從下列規(guī)則推導(dǎo)出來(lái)的話(huà)恨搓,它們就沒(méi)有順序性保障院促,虛擬機(jī)可以對(duì)它們隨意的進(jìn)行重排序。
- 程序次序規(guī)則:在一個(gè)線(xiàn)程內(nèi)斧抱,按照程序代碼順序常拓,書(shū)寫(xiě)在前面的操作先行發(fā)生于書(shū)寫(xiě)在后面的操作。準(zhǔn)備的說(shuō)辉浦,應(yīng)該是控制流順序而不是程序代碼順序弄抬,因?yàn)橐紤]分支、循環(huán)等結(jié)構(gòu)宪郊。
- 管程鎖定規(guī)則:一個(gè) unlock 操作先行發(fā)生于后面對(duì)同一個(gè)鎖的 lock 操作掂恕。這里必須強(qiáng)調(diào)的是同一個(gè)鎖,而“后面”是指時(shí)間上的先后順序废膘。
- volatile 變量規(guī)則:對(duì)一個(gè) volatile 變量的寫(xiě)操作先行發(fā)生于后面對(duì)這個(gè)變量的讀操作竹海,這里的“后面”同樣是指時(shí)間上的先后順序。
- 線(xiàn)程啟動(dòng)規(guī)則:Thread 對(duì)象的 start() 方法先行發(fā)生于此線(xiàn)程的每一個(gè)動(dòng)作丐黄。
- 線(xiàn)程終止規(guī)則:線(xiàn)程中的所有操作都先行于對(duì)此線(xiàn)程的終止檢測(cè)斋配,我們可以通過(guò) Thread.join() 方法結(jié)束、Thead.isAlive() 的返回值等手段檢測(cè)到線(xiàn)程已經(jīng)終止執(zhí)行灌闺。
- 線(xiàn)程中斷規(guī)則:對(duì)線(xiàn)程 interrupt() 方法的調(diào)用先行發(fā)生于被中斷線(xiàn)程的代碼檢測(cè)到中斷事件的發(fā)生艰争,可以通過(guò) Thread.interrupted() 方法檢測(cè)到是否有中斷發(fā)生。
- 對(duì)象終結(jié)規(guī)則:一個(gè)對(duì)象的初始化完成(構(gòu)造函數(shù)執(zhí)行結(jié)束)先行于發(fā)生于它的 finalized() 方法的開(kāi)始桂对。
- 傳遞性:如果操作 A 先行發(fā)生于操作 B甩卓,操作 B 先行發(fā)生于操作 C,那就可以得出操作 A 先行發(fā)生于操作 C 的結(jié)論蕉斜。