synchronized是面試中經(jīng)常會被問到的知識點,相關(guān)的問題點也很多纸肉,問題答案涉及的知識點也很多溺欧,有經(jīng)驗的面試官就會順著你的答案不斷追問一下喊熟,下面的對話場景就是相關(guān)面試題的連環(huán)炮。
面試官:說一下synchronized的作用姐刁。
小白:對于單一JVM來說芥牌,synchronized可以保證在并發(fā)情況下,同一時刻只有一個線程執(zhí)行某個方法或某段代碼聂使,它可用于修飾方法或代碼塊壁拉,實現(xiàn)對同步代碼的并發(fā)安全控制。
面試官:你剛剛說synchronized可用于修飾方法和代碼塊柏靶,他們有什么區(qū)別呢弃理?
小白:修飾方法在底層實現(xiàn)上會在方法訪問標(biāo)識中設(shè)置ACC_SYNCHRONIZED標(biāo)示符,修飾代碼塊在底層實現(xiàn)上會使用monitorenter和monitorexit指令宿礁。
面試官:那你說一下修飾方法方式的底層實現(xiàn)原理案铺?
小白:反編譯字節(jié)碼文件,可以看到在方法的flags中設(shè)置了ACC_SYNCHRONIZED訪問標(biāo)識梆靖。每個對象都與一個monitor相關(guān)聯(lián),當(dāng)且僅當(dāng)monitor被線程持有時笔诵,monitor處于鎖定狀態(tài)返吻。當(dāng)方法執(zhí)行時,線程將先嘗試獲取對象相關(guān)聯(lián)的monitor所有權(quán)乎婿,然后再執(zhí)行方法测僵,最后在方法完成(無論是正常執(zhí)行還是非正常執(zhí)行)時釋放monitor所有權(quán)。在方法執(zhí)行期間谢翎,線程持有了monitor所有權(quán)捍靠,其它任何線程都無法再獲得同一個對象相關(guān)聯(lián)的monitor所有權(quán)。
上面的答案會引發(fā)面試官提問Java對象頭和鎖相關(guān)的問題森逮,需要有心理準(zhǔn)備榨婆。
面試官:那你再說一下修飾代碼塊方式的底層實現(xiàn)原理?
小白:反編譯字節(jié)碼文件褒侧,可以看到在邏輯代碼前添加了monitorenter指令良风,在邏輯代碼尾添加了monitorexit指令。當(dāng)方法執(zhí)行時闷供,當(dāng)前線程執(zhí)行monitorenter指令嘗試獲取對象相關(guān)聯(lián)的monitor所有權(quán)時烟央,如果此時這個monitor的計數(shù)器是0,那么當(dāng)前線程持有該monitor歪脏,同時monitor計數(shù)器設(shè)置為1疑俭;如果當(dāng)前線程已經(jīng)持有了對象相關(guān)聯(lián)的monitor所有權(quán),只是想重新獲取婿失,那么繼續(xù)持有該monitor钞艇,同時monitor計數(shù)器加1啄寡;如果有其它線程已經(jīng)持有了對象相關(guān)聯(lián)的monitor所有權(quán),當(dāng)前線程阻塞香璃,直到monitor計數(shù)器為0这难,再次嘗試獲取所有權(quán)。方法正常執(zhí)行或發(fā)生異常時葡秒,會執(zhí)行monitorexit指令姻乓,釋放monitor所有權(quán),monitor計數(shù)器減1眯牧。
面試官:你剛剛說到了Monitor蹋岩,能詳細(xì)再說說嗎?
小白:Java虛擬機中学少,synchronized支持的同步方法和同步語句都是使用monitor來實現(xiàn)的剪个。每個對象都與一個monitor相關(guān)聯(lián),當(dāng)一個線程執(zhí)行到一個monitor監(jiān)視下的代碼塊中的第一個指令時版确,該線程必須在引用的對象上獲得一個鎖扣囊,這個鎖是monitor實現(xiàn)的。在HotSpot虛擬機中绒疗,monitor是由ObjectMonitor實現(xiàn)侵歇,使用C++編寫實現(xiàn),具體代碼在HotSpot虛擬機源碼ObjectMonitor.hpp文件中吓蘑。
查看源碼會發(fā)現(xiàn)惕虑,主要的屬性有_count(記錄該線程獲取鎖的次數(shù))、_recursions(鎖的重入次數(shù))磨镶、_owner(指向持有ObjectMonitor對象的線程)溃蔫、_WaitSet(處于wait狀態(tài)的線程集合)、_EntryList(處于等待鎖block狀態(tài)的線程隊列)琳猫。
當(dāng)并發(fā)線程執(zhí)行synchronized修飾的方法或語句塊時伟叛,先進(jìn)入_EntryList中,當(dāng)某個線程獲取到對象的monitor后沸移,把monitor對象中的_owner變量設(shè)置為當(dāng)前線程痪伦,同時monitor對象中的計數(shù)器_count加1,當(dāng)前線程獲取同步鎖成功雹锣。
當(dāng)synchronized修飾的方法或語句塊中的線程調(diào)用wait()方法時网沾,當(dāng)前線程將釋放持有的monitor對象,monitor對象中的_owner變量賦值為null蕊爵,同時辉哥,monitor對象中的_count值減1,然后當(dāng)前線程進(jìn)入_WaitSet集合中等待被喚醒。
面試官:你的回答中說到了鎖醋旦,那一個對象的鎖狀態(tài)存在哪里恒水?
小白:Java對象的對象頭中。
面試官:對象頭中包含哪些內(nèi)容饲齐?
小白:一部分是對象自身的運行時數(shù)據(jù)钉凌,如哈希碼(HashCode)、GC分代年齡捂人、鎖狀態(tài)標(biāo)志御雕、線程持有的鎖、偏向線程ID滥搭、偏向時間戳等酸纲,官方稱它為“Mark Word”;一部分是類型指針瑟匆,即是對象指向它的類的元數(shù)據(jù)的指針闽坡,虛擬機通過這個指針來確定這個對象是哪個類的實例;如果對象是一個Java數(shù)組愁溜,那在對象頭中還必須有一塊用于記錄數(shù)組長度的數(shù)據(jù)疾嗅,因為虛擬機可以通過普通Java對象的元數(shù)據(jù)信息確定Java對象的大小,但是從數(shù)組的元數(shù)據(jù)中無法確定數(shù)組的大小冕象。
面試官:對對象頭中的鎖狀態(tài)標(biāo)識來說宪迟,synchronized屬于哪一級別的鎖?
小白:重量級鎖交惯。
面試官:JVM對鎖進(jìn)行了哪些優(yōu)化?
小白:......