Java代碼在編譯后會(huì)變成Java字節(jié)碼内舟,字節(jié)碼被類(lèi)加載器加載到JVM里墓塌,JVM執(zhí)行字節(jié)碼羹铅,最終需要轉(zhuǎn)化為匯編指令在CPU上執(zhí)行霞捡,Java中所使用的并發(fā)機(jī)制依賴于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)前線程。