新聞中心
我是一個(gè)線程,生活在JVM(Java虛擬機(jī))中, 這一段日子過(guò)得有些無(wú)聊,整個(gè)世界似乎只有這一個(gè)人,天天忙著執(zhí)行代碼,想休息一下都很難。

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛(ài)。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶,將通過(guò)不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:空間域名、虛擬空間、營(yíng)銷軟件、網(wǎng)站建設(shè)、隨州網(wǎng)站維護(hù)、網(wǎng)站推廣。
我聽(tīng)說(shuō)人類寫的代碼中有些特殊的地方,叫做臨界區(qū),比如synchronized修飾的方法或者代碼塊,他們非常神奇,在同一時(shí)刻JVM老大只允許一個(gè)線程進(jìn)入執(zhí)行。
實(shí)際上,老大設(shè)置了一把鎖,搶到了這把鎖就可以執(zhí)行,否則只能阻塞,等待別人釋放鎖。
老大說(shuō),阻塞就是不用干活了,老老實(shí)實(shí)地等著就行。
竟然還有這等美事! 趕緊讓我阻塞一次吧。
可是老大又說(shuō):“每次設(shè)置鎖我都得和操作系統(tǒng)打交道,請(qǐng)他在內(nèi)核中維護(hù)一個(gè)什么Mutex(互斥量)的東西,他還得把你們這些線程阻塞,切換,這可是一筆巨大的費(fèi)用啊,所以這些鎖還是少用為妙?!?/p>
我運(yùn)氣也不好,我不知道執(zhí)行了多少代碼,調(diào)用了多少函數(shù),竟然一次也沒(méi)遇到臨界區(qū)!
我想也許這個(gè)程序員編程時(shí)不小心,沒(méi)有考慮多線程并發(fā)的情況; 也有可能是這些程序大部分都是無(wú)狀態(tài)的,多少個(gè)線程執(zhí)行都沒(méi)有問(wèn)題。
于是我只好一直執(zhí)行下去, 不知道過(guò)了多少天,我激動(dòng)地發(fā)現(xiàn),一個(gè)synchronized修飾的代碼塊終于出現(xiàn)了:
- Account account = ...
- synchronized(account){
- ...臨界區(qū)的代碼...
- }
偏向鎖
我滿心期望別的線程已經(jīng)進(jìn)入了代碼塊,那我就可以阻塞、休息。
即使沒(méi)有其他線程進(jìn)入臨界區(qū),老大為我申請(qǐng)鎖, 也得和操作系統(tǒng)協(xié)商什么互斥量,從用戶態(tài)進(jìn)入核心態(tài),再?gòu)暮诵膽B(tài)返回用戶態(tài),總要花些功夫吧。
可是老大根本沒(méi)有去找操作系統(tǒng), 只是看了看這個(gè)account對(duì)象的所謂“對(duì)象頭”,其中有個(gè)叫做Mark Word的東西,似乎是個(gè)什么數(shù)據(jù)結(jié)構(gòu), 里邊有幾個(gè)標(biāo)識(shí)位,還有其他數(shù)據(jù)。
老大隨手使用CAS操作把我的線程ID記錄到了這個(gè)Mark Word當(dāng)中,修改了標(biāo)識(shí)位,然后告訴我說(shuō): 可以了,你現(xiàn)在擁有這把鎖了,進(jìn)去執(zhí)行代碼吧。
我驚奇地說(shuō):“老大你不和操作系統(tǒng)協(xié)商設(shè)置Mutex了?”
老大說(shuō):“不用了,你看現(xiàn)在就你一個(gè)線程進(jìn)入了這個(gè)代碼塊,我只要記錄下你的線程ID,就表示你擁有這把鎖了,不用操作系統(tǒng)介入?!?/p>
我獲得了鎖,開(kāi)始執(zhí)行被synchronized包裹的代碼塊。
等到我第二次執(zhí)行到這個(gè)synchronized的時(shí)候,老大只是看了一眼鎖對(duì)象account的Mark Word就說(shuō):“你的線程ID還在,還持有著這個(gè)對(duì)象的鎖,進(jìn)入臨界區(qū)執(zhí)行吧?!?/p>
我連喘口氣的機(jī)會(huì)都沒(méi)有,只好繼續(xù)執(zhí)行。
老大說(shuō),這叫偏向鎖,在沒(méi)有別的線程競(jìng)爭(zhēng)的時(shí)候,一直偏向我,可以讓我一直執(zhí)行下去。
我是多么期盼來(lái)一個(gè)新的線程來(lái)和我競(jìng)爭(zhēng)??!
輕量級(jí)鎖
很快,機(jī)會(huì)就來(lái)了。
另外一個(gè)線程0x3704也要進(jìn)入這個(gè)代碼塊執(zhí)行,但是鎖對(duì)象account 保存的是我的線程ID,他是沒(méi)法進(jìn)入臨界區(qū)的。
我心想,我們兩個(gè)至少得有一個(gè)進(jìn)入阻塞狀態(tài),休息一會(huì)兒了。
但是老大還是不去操作系統(tǒng)協(xié)商,只是說(shuō): 我把這個(gè)偏向鎖升級(jí)一下,變成一個(gè)輕量級(jí)的鎖吧。
老大把鎖對(duì)象account恢復(fù)成無(wú)鎖狀態(tài),在我們倆的棧幀中各自分配了一個(gè)空間,叫做Lock Record, 把鎖對(duì)象account的Mark Word在我們倆這里各自復(fù)制了一份,叫做Displaced Mark Word, 這名字真奇怪。
然后把我的Lock Record的地址使用CAS放到了Mark Word當(dāng)中,并且把鎖標(biāo)志位改為00, 這其實(shí)就意味著我也已經(jīng)獲得了這個(gè)輕量級(jí)的鎖了,可以繼續(xù)進(jìn)入臨界區(qū)執(zhí)行。
0x3704沒(méi)有獲得鎖,但還是不阻塞,老大讓他自旋幾次,等待一會(huì)兒。
等到我退出臨界區(qū),釋放鎖的時(shí)候,需要把這個(gè)Displaced markd word 使用CAS復(fù)制回去。接下來(lái)他就可以加鎖了。
我們兩個(gè)交替著進(jìn)入臨界區(qū),執(zhí)行這段代碼,相安無(wú)事,很少出現(xiàn)真正的競(jìng)爭(zhēng)。
即使是出現(xiàn)了競(jìng)爭(zhēng),想獲得鎖的線程只要自旋幾次,等待一會(huì)兒,鎖就可能釋放了。
很明顯,如果沒(méi)有競(jìng)爭(zhēng)或者輕度的競(jìng)爭(zhēng),輕量級(jí)鎖僅僅使用CAS操作和Lock record就避免了重量級(jí)互斥鎖的開(kāi)銷,對(duì)JVM老大來(lái)說(shuō),確實(shí)是個(gè)好主意。
重量級(jí)鎖
輕量級(jí)鎖運(yùn)行得挺好,我還是沒(méi)有機(jī)會(huì)休息,終于有這么一天,0x3704 正在持有鎖,在臨界區(qū)辛苦地執(zhí)行代碼。 我自旋了好多次,0x3704還是沒(méi)釋放鎖。 這時(shí)候JVM老大說(shuō): 自旋次數(shù)太多了,太浪費(fèi)CPU了,接下來(lái)升級(jí)為重量級(jí)鎖!
這個(gè)重量級(jí)鎖需要操作系統(tǒng)的幫忙,依賴操作系統(tǒng)底層的Mutex Lock。
只見(jiàn)老大創(chuàng)建了一個(gè)monitor 對(duì)象, 把這個(gè)對(duì)象的地址更新到了Mark word當(dāng)中。
鎖升級(jí)了!
由于0x3704還在持有鎖運(yùn)行,而我終于進(jìn)入了夢(mèng)寐以求的狀態(tài):阻塞! 終于可以休息一下了!
仔細(xì)一想,老大煞費(fèi)心機(jī)地設(shè)置了偏向鎖和輕量級(jí)鎖,就是為了避免阻塞,避免操作系統(tǒng)的介入, 這兩種鎖無(wú)非就是針對(duì)這兩種情況:
偏向鎖: 通常只有一個(gè)線程在臨界區(qū)執(zhí)行
輕量級(jí)鎖: 可以有多個(gè)線程交替進(jìn)入臨界區(qū),在競(jìng)爭(zhēng)不激烈的時(shí)候,稍微自旋等待一下就能獲得鎖。
至于重量級(jí)鎖,也是我最為期待的鎖,那就是出現(xiàn)了激烈的競(jìng)爭(zhēng),只好讓我們?nèi)プ枞菹⒘恕?/p>
糾錯(cuò)!jvm專家 你假笨指出一個(gè)錯(cuò)誤 ,偏向鎖對(duì)象頭里存的不是線程id ,其實(shí)存的是JavaThread對(duì)象的指針地址。圖片沒(méi)法修改了,在此更正
【本文為專欄作者“劉欣”的原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)通過(guò)作者微信公眾號(hào)coderising獲取授權(quán)】
名稱欄目:一個(gè)想休息的線程:JVM到底是怎么處理鎖的?怎么不讓我阻塞呢?
文章出自:http://www.dlmjj.cn/article/dpjpsje.html


咨詢
建站咨詢
