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

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


咨詢
建站咨詢
