偏向鎖
偏向鎖是JDK6中的重要引進(jìn),因?yàn)镠otSpot作者經(jīng)過(guò)研究實(shí)踐發(fā)現(xiàn),在大多數(shù)情況下,鎖不僅不存在多線(xiàn)程競(jìng)爭(zhēng),而且總是由同一線(xiàn)程多次獲得,為了讓線(xiàn)程獲得鎖的代價(jià)更低,引進(jìn)了偏向鎖。
偏向鎖是在單線(xiàn)程執(zhí)行代碼塊時(shí)使用的機(jī)制,如果在多線(xiàn)程并發(fā)的環(huán)境下(即線(xiàn)程A尚未執(zhí)行完同步代碼塊,線(xiàn)程B發(fā)起了申請(qǐng)鎖的申請(qǐng)),則一定會(huì)轉(zhuǎn)化為輕量級(jí)鎖或者重量級(jí)鎖。
在JDK5中偏向鎖默認(rèn)是關(guān)閉的,而到了JDK6中偏向鎖已經(jīng)默認(rèn)開(kāi)啟。如果并發(fā)數(shù)較大同時(shí)同步代碼塊執(zhí)行時(shí)間較長(zhǎng),則被多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)的概率就很大,就可以使用參數(shù)-XX:-UseBiasedLocking來(lái)禁止偏向鎖(但這是個(gè)JVM參數(shù),不能針對(duì)某個(gè)對(duì)象鎖來(lái)單獨(dú)設(shè)置)。
引入偏向鎖主要目的是:為了在沒(méi)有多線(xiàn)程競(jìng)爭(zhēng)的情況下盡量減少不必要的輕量級(jí)鎖執(zhí)行路徑。因?yàn)檩p量級(jí)鎖的加鎖解鎖操作是需要依賴(lài)多次CAS原子指令的,而偏向鎖只需要在置換ThreadID的時(shí)候依賴(lài)一次CAS原子指令(由于一旦出現(xiàn)多線(xiàn)程競(jìng)爭(zhēng)的情況就必須撤銷(xiāo)偏向鎖,所以偏向鎖的撤銷(xiāo)操作的性能損耗也必須小于節(jié)省下來(lái)的CAS原子指令的性能消耗)。
輕量級(jí)鎖是為了在線(xiàn)程交替執(zhí)行同步塊時(shí)提高性能,而偏向鎖則是在只有一個(gè)線(xiàn)程執(zhí)行同步塊時(shí)進(jìn)一步提高性能。
那么偏向鎖是如何來(lái)減少不必要的CAS操作呢?首先我們看下無(wú)競(jìng)爭(zhēng)下鎖存在什么問(wèn)題:
現(xiàn)在幾乎所有的鎖都是可重入的,即已經(jīng)獲得鎖的線(xiàn)程可以多次鎖住/解鎖監(jiān)視對(duì)象,按照之前的HotSpot設(shè)計(jì),每次加鎖/解鎖都會(huì)涉及到一些CAS操作(比如對(duì)等待隊(duì)列的CAS操作),CAS操作會(huì)延遲本地調(diào)用,因此偏向鎖的想法是?一旦線(xiàn)程第一次獲得了監(jiān)視對(duì)象,之后讓監(jiān)視對(duì)象“偏向”這個(gè)線(xiàn)程,之后的多次調(diào)用則可以避免CAS操作,說(shuō)白了就是置個(gè)變量,如果發(fā)現(xiàn)為true則無(wú)需再走各種加鎖/解鎖流程。
CAS為什么會(huì)引入本地延遲?這要從SMP(對(duì)稱(chēng)多處理器)架構(gòu)說(shuō)起,下圖大概表明了SMP的結(jié)構(gòu):
SMP(對(duì)稱(chēng)多處理器)架構(gòu)
其意思是?所有的CPU會(huì)共享一條系統(tǒng)總線(xiàn)(BUS),靠此總線(xiàn)連接主存。每個(gè)核都有自己的一級(jí)緩存,各核相對(duì)于BUS對(duì)稱(chēng)分布,因此這種結(jié)構(gòu)稱(chēng)為“對(duì)稱(chēng)多處理器”。
而CAS的全稱(chēng)為Compare-And-Swap,是一條CPU的原子指令,其作用是讓CPU比較后原子地更新某個(gè)位置的值,經(jīng)過(guò)調(diào)查發(fā)現(xiàn),其實(shí)現(xiàn)方式是基于硬件平臺(tái)的匯編指令,就是說(shuō)CAS是靠硬件實(shí)現(xiàn)的,JVM只是封裝了匯編調(diào)用,那些AtomicInteger類(lèi)便是使用了這些封裝后的接口。
例如:Core1和Core2可能會(huì)同時(shí)把主存中某個(gè)位置的值Load到自己的L1 Cache中,當(dāng)Core1在自己的L1 Cache中修改這個(gè)位置的值時(shí),會(huì)通過(guò)總線(xiàn),使Core2中L1 Cache對(duì)應(yīng)的值“失效”,而Core2一旦發(fā)現(xiàn)自己L1 Cache中的值失效(稱(chēng)為Cache命中缺失)則會(huì)通過(guò)總線(xiàn)從內(nèi)存中加載該地址最新的值,大家通過(guò)總線(xiàn)的來(lái)回通信稱(chēng)為“Cache一致性流量”,因?yàn)榭偩€(xiàn)被設(shè)計(jì)為固定的“通信能力”,如果Cache一致性流量過(guò)大,總線(xiàn)將成為瓶頸。而當(dāng)Core1和Core2中的值再次一致時(shí),稱(chēng)為“Cache一致性”,從這個(gè)層面來(lái)說(shuō),鎖設(shè)計(jì)的終極目標(biāo)便是減少Cache一致性流量。
而CAS恰好會(huì)導(dǎo)致Cache一致性流量,如果有很多線(xiàn)程都共享同一個(gè)對(duì)象,當(dāng)某個(gè)Core CAS成功時(shí)必然會(huì)引起總線(xiàn)風(fēng)暴,這就是所謂的本地延遲,本質(zhì)上偏向鎖就是為了消除CAS,降低Cache一致性流量。
Cache一致性:
上面提到Cache一致性,其實(shí)是有協(xié)議支持的,現(xiàn)在通用的協(xié)議是MESI(最早由Intel開(kāi)始支持),具體參考:http://en.wikipedia.org/wiki/MESI_protocol。
Cache一致性流量的例外情況:
其實(shí)也不是所有的CAS都會(huì)導(dǎo)致總線(xiàn)風(fēng)暴,這跟Cache一致性協(xié)議有關(guān),具體參考:http://blogs.oracle.com/dave/entry/biased_locking_in_hotspot
NUMA(Non Uniform Memory Access Achitecture)架構(gòu):
與SMP對(duì)應(yīng)還有非對(duì)稱(chēng)多處理器架構(gòu),現(xiàn)在主要應(yīng)用在一些高端處理器上,主要特點(diǎn)是沒(méi)有總線(xiàn),沒(méi)有公用主存,每個(gè)Core有自己的內(nèi)存,針對(duì)這種結(jié)構(gòu)此處不做討論。
所以,當(dāng)一個(gè)線(xiàn)程訪(fǎng)問(wèn)同步塊并獲取鎖時(shí),會(huì)在對(duì)象頭和棧幀中的鎖記錄里存儲(chǔ)鎖偏向的線(xiàn)程ID,以后該線(xiàn)程進(jìn)入和退出同步塊時(shí)不需要花費(fèi)CAS操作來(lái)爭(zhēng)奪鎖資源,只需要檢查是否為偏向鎖、鎖標(biāo)識(shí)為以及ThreadID即可,處理流程如下:
- 檢測(cè)Mark Word是否為可偏向狀態(tài),即是否為偏向鎖1,鎖標(biāo)識(shí)位為01;
- 若為可偏向狀態(tài),則測(cè)試線(xiàn)程ID是否為當(dāng)前線(xiàn)程ID,如果是,則執(zhí)行步驟(5),否則執(zhí)行步驟(3);
- 如果測(cè)試線(xiàn)程ID不為當(dāng)前線(xiàn)程ID,則通過(guò)CAS操作競(jìng)爭(zhēng)鎖,競(jìng)爭(zhēng)成功,則將Mark Word的線(xiàn)程ID替換為當(dāng)前線(xiàn)程ID,否則執(zhí)行線(xiàn)程(4);
- 通過(guò)CAS競(jìng)爭(zhēng)鎖失敗,證明當(dāng)前存在多線(xiàn)程競(jìng)爭(zhēng)情況,當(dāng)?shù)竭_(dá)全局安全點(diǎn),獲得偏向鎖的線(xiàn)程被掛起,偏向鎖升級(jí)為輕量級(jí)鎖,然后被阻塞在安全點(diǎn)的線(xiàn)程繼續(xù)往下執(zhí)行同步代碼塊;
- 執(zhí)行同步代碼塊;
偏向鎖的釋放采用了?一種只有競(jìng)爭(zhēng)才會(huì)釋放鎖的機(jī)制,線(xiàn)程是不會(huì)主動(dòng)去釋放偏向鎖,需要等待其他線(xiàn)程來(lái)競(jìng)爭(zhēng)。偏向鎖的撤銷(xiāo)需要?等待全局安全點(diǎn)(這個(gè)時(shí)間點(diǎn)上是沒(méi)有正在執(zhí)行的代碼)。其步驟如下:
- 暫停擁有偏向鎖的線(xiàn)程;
- 判斷鎖對(duì)象是否還處于被鎖定狀態(tài),否,則恢復(fù)到無(wú)鎖狀態(tài)(01),以允許其余線(xiàn)程競(jìng)爭(zhēng)。是,則掛起持有鎖的當(dāng)前線(xiàn)程,并將指向當(dāng)前線(xiàn)程的鎖記錄地址的指針放入對(duì)象頭Mark Word,升級(jí)為輕量級(jí)鎖狀態(tài)(00),然后恢復(fù)持有鎖的當(dāng)前線(xiàn)程,進(jìn)入輕量級(jí)鎖的競(jìng)爭(zhēng)模式;
注意:此處將?當(dāng)前線(xiàn)程掛起再恢復(fù)的過(guò)程中并沒(méi)有發(fā)生鎖的轉(zhuǎn)移,仍然在當(dāng)前線(xiàn)程手中,只是穿插了個(gè)?“將對(duì)象頭中的線(xiàn)程ID變更為指向鎖記錄地址的指針”?這么個(gè)事。
偏向鎖的獲取和釋放過(guò)程
輕量級(jí)鎖
引入輕量級(jí)鎖的主要目的是?在沒(méi)有多線(xiàn)程競(jìng)爭(zhēng)的前提下,減少傳統(tǒng)的重量級(jí)鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗。當(dāng)關(guān)閉偏向鎖功能或者多個(gè)線(xiàn)程競(jìng)爭(zhēng)偏向鎖導(dǎo)致偏向鎖升級(jí)為輕量級(jí)鎖,則會(huì)嘗試獲取輕量級(jí)鎖,其步驟如下:
在線(xiàn)程進(jìn)入同步塊時(shí),如果同步對(duì)象鎖狀態(tài)為無(wú)鎖狀態(tài)(鎖標(biāo)志位為“01”狀態(tài),是否為偏向鎖為“0”),虛擬機(jī)首先將在當(dāng)前線(xiàn)程的棧幀中建立一個(gè)名為鎖記錄(Lock Record)的空間,用于存儲(chǔ)鎖對(duì)象目前的Mark Word的拷貝,官方稱(chēng)之為 Displaced Mark Word。此時(shí)線(xiàn)程堆棧與對(duì)象頭的狀態(tài)如下圖所示:
????????????????????????????????輕量級(jí)鎖CAS操作之前線(xiàn)程堆棧與對(duì)象的狀態(tài)
-
拷貝對(duì)象頭中的Mark Word復(fù)制到鎖記錄(Lock Record)中;
-
拷貝成功后,虛擬機(jī)將使用CAS操作嘗試將對(duì)象Mark Word中的Lock Word更新為指向當(dāng)前線(xiàn)程Lock Record的指針,并將Lock record里的owner指針指向object mark word。(Lock Word屬于線(xiàn)程,對(duì)象中的Lock Word是指向線(xiàn)程Lock Word的指針,線(xiàn)程Lock Word 記錄對(duì)象的Object mark word, 相當(dāng)于 對(duì)象和線(xiàn)程相互關(guān)聯(lián))
-
如果這個(gè)更新動(dòng)作成功了,那么當(dāng)前線(xiàn)程就擁有了該對(duì)象的鎖,并且對(duì)象Mark Word的鎖標(biāo)志位設(shè)置為“00”,即表示此對(duì)象處于輕量級(jí)鎖定狀態(tài),此時(shí)線(xiàn)程堆棧與對(duì)象頭的狀態(tài)如下圖所示:
????????????????????????????????輕量級(jí)鎖CAS操作之后線(xiàn)程堆棧與對(duì)象的狀態(tài)
-
如果這個(gè)更新操作失敗了,虛擬機(jī)首先會(huì)檢查對(duì)象Mark Word中的Lock Word是否指向當(dāng)前線(xiàn)程的棧幀,如果是,就說(shuō)明當(dāng)前線(xiàn)程已經(jīng)擁有了這個(gè)對(duì)象的鎖,那就可以直接進(jìn)入同步塊繼續(xù)執(zhí)行。否則說(shuō)明多個(gè)線(xiàn)程競(jìng)爭(zhēng)鎖,進(jìn)入自旋執(zhí)行,若自旋結(jié)束時(shí)仍未獲得鎖,輕量級(jí)鎖就要膨脹為重量級(jí)鎖,鎖標(biāo)志的狀態(tài)值變?yōu)椤?strong>10”,Mark Word中存儲(chǔ)的就是指向重量級(jí)鎖(互斥量)的指針,當(dāng)前線(xiàn)程以及后面等待鎖的線(xiàn)程也要進(jìn)入阻塞狀態(tài)。
輕量級(jí)鎖的釋放也是通過(guò)CAS操作來(lái)進(jìn)行的,主要步驟如下:
- 通過(guò)CAS操作嘗試把線(xiàn)程中復(fù)制的Displaced Mark Word對(duì)象替換當(dāng)前的Mark Word;
- 如果替換成功,整個(gè)同步過(guò)程就完成了,恢復(fù)到無(wú)鎖狀態(tài)(01);
- 如果替換失敗,說(shuō)明有其他線(xiàn)程嘗試過(guò)獲取該鎖(此時(shí)鎖已膨脹),那就要在釋放鎖的同時(shí),喚醒被掛起的線(xiàn)程;
對(duì)于輕量級(jí)鎖,其性能提升的依據(jù)是?“對(duì)于絕大部分的鎖,在整個(gè)生命周期內(nèi)都是不會(huì)存在競(jìng)爭(zhēng)的”,如果打破這個(gè)依據(jù)則除了互斥的開(kāi)銷(xiāo)外,還有額外的CAS操作,因此在有多線(xiàn)程競(jìng)爭(zhēng)的情況下,輕量級(jí)鎖比重量級(jí)鎖更慢。
????????????????????????????????????????????????輕量級(jí)鎖的獲取和釋放過(guò)程
-
為什么升級(jí)為輕量鎖時(shí)要把對(duì)象頭里的Mark Word復(fù)制到線(xiàn)程棧的鎖記錄中呢?
因?yàn)樵谏暾?qǐng)對(duì)象鎖時(shí)?需要以該值作為CAS的比較條件,同時(shí)在升級(jí)到重量級(jí)鎖的時(shí)候,能通過(guò)這個(gè)比較判定是否在持有鎖的過(guò)程中此鎖被其他線(xiàn)程申請(qǐng)過(guò),如果被其他線(xiàn)程申請(qǐng)了,則在釋放鎖的時(shí)候要喚醒被掛起的線(xiàn)程。
-
為什么會(huì)嘗試CAS不成功以及什么情況下會(huì)不成功?
CAS本身是不帶鎖機(jī)制的,其是通過(guò)比較而來(lái)。假設(shè)如下場(chǎng)景:線(xiàn)程A和線(xiàn)程B都在對(duì)象頭里的鎖標(biāo)識(shí)為無(wú)鎖狀態(tài)進(jìn)入,那么如線(xiàn)程A先更新對(duì)象頭為其鎖記錄指針成功之后,線(xiàn)程B再用CAS去更新,就會(huì)發(fā)現(xiàn)此時(shí)的對(duì)象頭已經(jīng)不是其操作前的對(duì)象HashCode了,所以CAS會(huì)失敗。也就是說(shuō),只有兩個(gè)線(xiàn)程并發(fā)申請(qǐng)鎖的時(shí)候會(huì)發(fā)生CAS失敗。
然后線(xiàn)程B進(jìn)行CAS自旋,等待對(duì)象頭的鎖標(biāo)識(shí)重新變回?zé)o鎖狀態(tài)或?qū)ο箢^內(nèi)容等于對(duì)象HashCode(因?yàn)檫@是線(xiàn)程B做CAS操作前的值),這也就意味著線(xiàn)程A執(zhí)行結(jié)束(參見(jiàn)后面輕量級(jí)鎖的撤銷(xiāo),只有線(xiàn)程A執(zhí)行完畢撤銷(xiāo)鎖了才會(huì)重置對(duì)象頭),此時(shí)線(xiàn)程B的CAS操作終于成功了,于是線(xiàn)程B獲得了鎖以及執(zhí)行同步代碼的權(quán)限。如果線(xiàn)程A的執(zhí)行時(shí)間較長(zhǎng),線(xiàn)程B經(jīng)過(guò)若干次CAS時(shí)鐘沒(méi)有成功,則鎖膨脹為重量級(jí)鎖,即線(xiàn)程B被掛起阻塞、等待重新調(diào)度。
此處,如何理解“輕量級(jí)”?“輕量級(jí)”是相對(duì)于使用操作系統(tǒng)互斥量來(lái)實(shí)現(xiàn)的傳統(tǒng)鎖而言的。但是,首先需要強(qiáng)調(diào)一點(diǎn)的是,輕量級(jí)鎖并不是用來(lái)代替重量級(jí)鎖的,它的本意是在沒(méi)有多線(xiàn)程競(jìng)爭(zhēng)的前提下,減少傳統(tǒng)的重量級(jí)鎖使用產(chǎn)生的性能消耗。
輕量級(jí)鎖所適應(yīng)的場(chǎng)景是線(xiàn)程交替執(zhí)行同步塊的情況,如果存在同一時(shí)間訪(fǎng)問(wèn)同一鎖的情況,必然就會(huì)導(dǎo)致輕量級(jí)鎖膨脹為重量級(jí)鎖。
?重量級(jí)鎖
Synchronized是通過(guò)對(duì)象內(nèi)部的一個(gè)叫做?監(jiān)視器鎖(Monitor)來(lái)實(shí)現(xiàn)的。但是監(jiān)視器鎖本質(zhì)又是依賴(lài)于底層的操作系統(tǒng)的Mutex Lock來(lái)實(shí)現(xiàn)的。而操作系統(tǒng)實(shí)現(xiàn)線(xiàn)程之間的切換這就需要從用戶(hù)態(tài)轉(zhuǎn)換到核心態(tài),這個(gè)成本非常高,狀態(tài)之間的轉(zhuǎn)換需要相對(duì)比較長(zhǎng)的時(shí)間,這就是為什么Synchronized效率低的原因。因此,這種依賴(lài)于操作系統(tǒng)Mutex Lock所實(shí)現(xiàn)的鎖我們稱(chēng)之為?“重量級(jí)鎖”。
重量級(jí)鎖、輕量級(jí)鎖和偏向鎖之間轉(zhuǎn)換
????????????????????????????????重量級(jí)鎖、輕量級(jí)鎖和偏向鎖之間轉(zhuǎn)換
????????????????????????Synchronized偏向鎖、輕量級(jí)鎖及重量級(jí)鎖轉(zhuǎn)換流程
知識(shí)來(lái)源:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-687540.html
https://www.cnblogs.com/aspirant/p/11470858.html文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-687540.html
到了這里,關(guān)于java八股文面試[多線(xiàn)程]——synchronized鎖升級(jí)詳細(xì)流程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!