自旋鎖和自適應自旋
自旋鎖是當多個線程並行訪問共享數據時,用忙循環讓後面請求鎖的線程處於等待狀態,當自旋多次後仍然獲取不到鎖,再用傳統的方式將線程掛起。
忙循環就是用循環讓線程等待,忙循環也叫自旋。
在Java6之後,如果有線程剛通過自旋獲得了鎖,並且線程正在運行,那麼JVM
會認爲自旋獲取鎖的命中率高,其他線程的自旋次數可能會增多。如果某個鎖通過自旋獲得的次數少,JVM
可能會省略掉自旋獲取鎖,直接掛起線程去等待鎖。
鎖消除
鎖消除是指JVM
即時編譯器運行時,對一些代碼上要求同步,但是被檢測到不可能存在共享數據的競爭,那麼鎖會進行消除。
它判斷的依據是通過判斷一段代碼在堆上的所有數據不會逃逸,被其他線程訪問到,那麼認爲它是線程私有的,進行鎖消除。
鎖粗化
編寫代碼時,要求同步代碼塊的範圍越小越好。但是在某些情況下,例如在循環中調用StringBuffer#append
方法,這會對一個對象反覆加鎖,如果出現這類情況,JVM
會將鎖同步的範圍優化到整個操作的外面,讓程序加一次鎖就行了。
輕量級鎖和偏向鎖
輕量級鎖和偏向鎖都是通過對象頭信息中的鎖標誌位進行鎖優化。
在對象頭的Mark Word
中,有兩個位是鎖標誌位。00表示未鎖定、01表示輕量級鎖定、10表示重量級鎖。
-
輕量級鎖
輕量級鎖是指在代碼執行到同步塊時,如果同步對象未鎖定,
JVM
會在當前線程的棧幀中創建一個鎖記錄,然後使用CAS
將對象的Mark Word
更新爲執行鎖記錄的指針,如果操作成功,表示線程獲得鎖,將鎖標誌位變爲00。如果失敗,首先判斷對象的Mark Word
是否指向當前線程的棧幀,如果是,可以直接進入同步塊執行,否則說明鎖被其他線程佔有了。如果有兩條以上的線程爭用同一個鎖,那麼輕量級鎖就失效了,變爲重量級鎖10。 -
偏向鎖
偏向鎖是指鎖對象會偏向於第一個獲得它的線程,如果之後鎖對象不再被其他線程獲取,那麼持有偏向鎖的線程永遠不用再進行同步。當鎖對象第一次被線程獲取時,
JVM
將鎖標誌改爲01,並用CAS
把獲取到鎖的線程ID
記錄到對象的Mark Word
中。如果CAS
成功,持有偏向鎖的線程以後每次執行同步塊時,JVM
都不會有任何同步操作。但是當有另外的線程嘗試獲取這個鎖時,偏向模式就結束了。