Java并發(fā)機(jī)制的底層實(shí)現(xiàn)原理

Java代碼在編譯后會(huì)變成Java字節(jié)碼,字節(jié)碼被類(lèi)加載器加載到JVM里,JVM執(zhí)行字節(jié)碼,最終需要轉(zhuǎn)化為匯編指令在CPU上執(zhí)行,Java中所使用的并發(fā)機(jī)制依賴(lài)于JVM的實(shí)現(xiàn)和CPU的指令。

volatile的理解

volatile是輕量級(jí)的synchronized,在多處理器開(kāi)發(fā)中保證了共享變量的“可見(jiàn)性”(當(dāng)一個(gè)線程修改一個(gè)共享變量時(shí),另一個(gè)線程可以能讀到這個(gè)修改的值)。相比synchronized使用的和執(zhí)行的成本低,因?yàn)樗粫?huì)引起線程上下文的切換和調(diào)度。

在volatile變量修飾的共享變量進(jìn)行寫(xiě)操作時(shí),會(huì)在多核處理器下引發(fā)兩件事:

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

原因:為了提高處理的速度,處理器不會(huì)與內(nèi)存直接進(jìn)行通信,而是把數(shù)據(jù)寫(xiě)到內(nèi)部的緩存再進(jìn)行操作,之后,處理器不知道何時(shí)再寫(xiě)到內(nèi)存。如果對(duì)聲明了volatile變量進(jìn)行行寫(xiě)操作,JVM就會(huì)向處理器發(fā)送一條LOCK前綴的指令,將這個(gè)變量所在的緩存行的數(shù)據(jù)寫(xiě)回系統(tǒng)內(nèi)存。在多處理器下,為了保證各個(gè)處理器的緩存是一致的,就會(huì)實(shí)現(xiàn)緩存一致性協(xié)議,每個(gè)處理器通過(guò)嗅探在總線上傳播的數(shù)據(jù)來(lái)檢查自己緩存的值是不是過(guò)期了,當(dāng)處理器發(fā)現(xiàn)自己緩存行對(duì)應(yīng)的內(nèi)存地址被修改,就會(huì)將當(dāng)前處理器的緩存行設(shè)置成無(wú)效狀態(tài),當(dāng)處理器對(duì)這個(gè)數(shù)據(jù)進(jìn)行修改操作的時(shí)候,會(huì)重新從系統(tǒng)內(nèi)存中把數(shù)據(jù)讀到處理器緩存里。

synchronized的實(shí)現(xiàn)原理與應(yīng)用

Java中每個(gè)對(duì)象都可以作為鎖

  • 對(duì)于普通同步方法,鎖是當(dāng)前實(shí)例對(duì)象。
  • 對(duì)于靜態(tài)同步方法,鎖是當(dāng)前類(lèi)的Class對(duì)象。
  • 對(duì)于同步代碼塊,鎖是synchronized括號(hào)里的配置對(duì)象。
實(shí)現(xiàn)原理

從JVM規(guī)范中可以看到Synchonized在JVM里的實(shí)現(xiàn)原理,JVM基于進(jìn)入和退出Monitor對(duì)象來(lái)實(shí)現(xiàn)方法同步和代碼塊同步,但兩者的實(shí)現(xiàn)細(xì)節(jié)不一樣。代碼塊同步是使用monitorenter和monitorexit指令實(shí)現(xiàn)的,而方法同步是使用另外一種方式實(shí)現(xiàn)的,細(xì)節(jié)在JVM規(guī)范里并沒(méi)有詳細(xì)說(shuō)明。但是,方法的同步同樣可以使用這兩個(gè)指令來(lái)實(shí)現(xiàn)。
monitorenter指令是在編譯后插入到同步代碼塊的開(kāi)始位置,而monitorexit是插入到方法結(jié)束處和異常處,JVM要保證每monitorenter必須有對(duì)應(yīng)的monitorexit與之配對(duì)。任何對(duì)象都有一個(gè)monitor與之關(guān)聯(lián),當(dāng)且一個(gè)monitor被持有后,它將處于鎖定狀態(tài)。線程執(zhí)行到monitorenter指令時(shí),將會(huì)嘗試獲取對(duì)象所對(duì)應(yīng)的monitor的所有權(quán),即嘗試獲得對(duì)象的鎖。

鎖的升級(jí)與對(duì)比

在Java SE1.6 為了減少獲得鎖和釋放鎖帶來(lái)的性能消耗,引入了“偏向鎖”和“輕量級(jí)鎖”,其中所具有四種狀態(tài),級(jí)別依次從低往高為:無(wú)鎖狀態(tài)、偏向鎖狀態(tài)、輕量級(jí)鎖狀態(tài)、重量級(jí)鎖狀態(tài)。(鎖可以升級(jí)不可降級(jí))

1.偏向鎖:

當(dāng)一個(gè)線程訪問(wèn)同步塊并獲取鎖時(shí),會(huì)在對(duì)象頭和棧幀中的鎖記錄里存儲(chǔ)鎖偏向的線程ID,以后該線程在進(jìn)入和退出同步塊時(shí)不需要進(jìn)行CAS操作(CAS是英文單詞CompareAndSwap的縮寫(xiě),中文意思是:比較并替換。CAS需要有3個(gè)操作數(shù):內(nèi)存地址V,舊的預(yù)期值A(chǔ),即將要更新的目標(biāo)值B。CAS指令執(zhí)行時(shí),當(dāng)且僅當(dāng)內(nèi)存地址V的值與預(yù)期值A(chǔ)相等時(shí),將內(nèi)存地址V的值修改為B,否則就什么都不做。整個(gè)比較并替換的操作是一個(gè)原子操作。)來(lái)加鎖和解鎖,只需簡(jiǎn)單地測(cè)試一下對(duì)象頭的Mark Word里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖。如果測(cè)試成功,表示線程已經(jīng)獲得了鎖。如果測(cè)試失敗,則需要再測(cè)試一下Mark Word中偏向鎖的標(biāo)識(shí)是否設(shè)置成1(表示當(dāng)前是偏向鎖):如果沒(méi)有設(shè)置,則使用CAS競(jìng)爭(zhēng)鎖;如果設(shè)置了,則嘗試使用CAS將對(duì)象頭的偏向鎖指向當(dāng)前線程。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容