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

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


咨詢
建站咨詢
