synchronized關鍵字
在java里芒炼,使用的最基本互斥同步手段就是synchronized關鍵字趴久。synchronized關鍵字經(jīng)過Javac編譯后廷臼,會在同步代碼塊的前后分別形成一個monitorEnter 和 monitorExit 兩個字節(jié)碼指令蝗柔。這兩個字節(jié)碼指令都需要一個reference類型的參數(shù)來指明要鎖定和解鎖的對象改含。如果代碼中synchronized明確指定了對象情龄,那么就以這個對象的引用作為reference。如果沒有指定捍壤,那么將根據(jù)synchronized所修飾的方法類型(實例還是static)來決定是取代碼所在的對象實例還是方法對應的Class對象來作為線程持有的鎖骤视。
當前線程在執(zhí)行monitorEnter指令時,首先要去嘗試獲取對象的鎖鹃觉,如果對象沒有被鎖定专酗,或者當前線程已經(jīng)持有了鎖,則會把鎖的計數(shù)器加一帜慢,而在執(zhí)行monitorExit 指令時會將鎖計數(shù)器減一笼裳。一旦鎖計數(shù)器的值為0,鎖就等于是被釋放了粱玲。如果獲取對象鎖失敗躬柬,那么當前線程就應當被阻塞等待,直到請求鎖定的對象被持有它的線程釋放為止抽减。
可以看出允青,持有鎖是一個重量級的操作,因為如果要阻塞和喚醒一個線程卵沉,則需要操作系統(tǒng)來幫忙颠锉,同時會發(fā)生用戶態(tài)與內(nèi)核態(tài)的轉(zhuǎn)換法牲,這種狀態(tài)的轉(zhuǎn)換會額外消耗處理時間,尤其是對于代碼特別簡單的同步代碼塊(比如被synchronized關鍵字修飾的get或set)琼掠,狀態(tài)轉(zhuǎn)換消耗的時間可能超過了代碼執(zhí)行的時間拒垃,為了優(yōu)化這一問題,JVM引用了鎖優(yōu)化機制瓷蛙。
鎖優(yōu)化機制
1.鎖消除
鎖消除是指虛擬機即時編譯器在運行時悼瓮,對一些代碼要求同步,但是對被檢測刀不可能存在共享數(shù)據(jù)競爭的鎖進行消除艰猬。鎖消除的主要判定依據(jù)來源于逃逸分析的數(shù)據(jù)支持横堡,如果判斷到一段代碼中,在堆上的所有數(shù)據(jù)都不會逃逸出去被其他線程訪問到冠桃,那么則認為它們是線程私有的命贴,即不會進行加鎖操作。例如使用StringBuffer時食听,StringBuffer的append()中都有一個synchronized同步代碼塊胸蛛,但是如果經(jīng)過逃逸分析得出當前的StringBuffer實例不會被其他線程訪問到的話,就不會進行加鎖操作碳蛋。
2.鎖粗化
對與一系列的連續(xù)操作都對同一個對象反復加鎖和解鎖胚泌,甚至加鎖操作出現(xiàn)在循環(huán)體之中,那即使沒有線程競爭肃弟,頻繁的進行互斥同步操作也會導致不必要的性能損耗玷室。所以為了使加鎖操作數(shù)量盡可能的小,會對一些連續(xù)的對同一個對象反復加鎖和解鎖的情況笤受,會將鎖同步的范圍擴大到整個操作外部穷缤。
3.輕量級鎖
輕量級鎖:在代碼即將進入同步塊的時候,JVM默認會進行輕量級鎖的上鎖:如果此同步對象沒有被鎖定箩兽,則JVM會在當前線程的棧幀中建立一個鎖記錄的空間津肛,用于儲存鎖對象的Mark Word的拷貝(對象頭中儲存HashCode和分代年齡和偏向模式的二進制碼)。然后虛擬機會采用CAS操作嘗試把Mark Word更新為指向鎖記錄空間的指針汗贫,同時將鎖標志位改為00身坐,如果這個操作成功了,則代表當前線程獲得了該鎖落包。如果更新操作失敗了部蛇,說明有別的線程在競爭獲取該對象的鎖。虛擬機首先會去檢查對象的Mark Word是否指向當前線程的棧幀中的鎖記錄空間咐蝇,如果是涯鲁,則說明當前線程已經(jīng)擁有了鎖,繼續(xù)執(zhí)行同步代碼塊即可。否則就說明對象已經(jīng)被其他線程搶占了抹腿,此時輕量級鎖就不再有效岛请,必須膨脹為重量級鎖(互斥鎖),此時鎖對象Mark Word會重新指向重量級鎖的指針警绩,并把鎖標志位改為10崇败。當不存在鎖競爭時,輕量級鎖通過CAS操作避免了使用互斥量的開銷肩祥,不過一旦有鎖競爭僚匆,不僅會有互斥量的開銷,也會有CSA操作帶來的開銷搭幻。(輕量級鎖膨脹為重量級鎖)
4.偏向鎖
偏向鎖(通過-XX:+UseBiasedLocking參數(shù)開啟):如果JVM開啟偏向鎖,那么當鎖對象第一次被線程獲取的時候逞盆,虛擬機會將對象頭中的鎖標志為改為01檀蹋,同時使用CAS操作把獲取到鎖的線程ID記錄在對象頭中的Mark Word中(占用hashcode的空間,即偏向鎖的特征是鎖標志位為01云芦,且無hashcode)俯逾。如果CAS操作成功,持有偏向鎖的線程以后每次進入這個鎖相關的同步塊時舅逸,虛擬機不再進行任何同步操作(加鎖桌肴、解鎖,對mark word更新)琉历。不過一旦出現(xiàn)另外一個線程嘗試獲取這個鎖的情況坠七,偏向鎖就不再生效了,會根據(jù)當前對象鎖是否被持有來決定撤銷偏向鎖后將鎖的標志位更新為01(未鎖定)還是00(輕量級鎖)旗笔,后續(xù)的同步操作則按照輕量級鎖的操作去執(zhí)行彪置,且當前對象不能再進入到偏向鎖狀態(tài)(擁有hashcode的對象,無法進入偏向鎖狀態(tài))蝇恶。(偏向鎖膨脹為輕量級鎖)