synchronized關鍵字就像是汽車的自動檔,現在詳細講這個過程。一腳油門踩下去,synchronized會從無鎖公升級為偏向鎖,再公升級為輕量級鎖,最後公升級為重量級鎖,就像自動換擋一樣。那麼自旋鎖在**呢?這裡的輕量級鎖就是一種自旋鎖。
初次執行到synchronized**塊的時候,鎖物件變成偏向鎖(通過cas修改物件頭里的鎖標誌位),字面意思是「偏向於第乙個獲得它的執行緒」的鎖。執行完同步**塊後,執行緒並不會主動釋放偏向鎖。當第二次到達同步**塊時,執行緒會判斷此時持有鎖的執行緒是否就是自己(持有鎖的執行緒id也在物件頭里),如果是則正常往下執行。由於之前沒有釋放鎖,這裡也就不需要重新加鎖。如果自始至終使用鎖的執行緒只有乙個,很明顯偏向鎖幾乎沒有額外開銷,效能極高。
一旦有第二個執行緒加入鎖競爭,偏向鎖就公升級為輕量級鎖(自旋鎖)。這裡要明確一下什麼是鎖競爭:如果多個執行緒輪流獲取乙個鎖,但是每次獲取鎖的時候都很順利,沒有發生阻塞,那麼就不存在鎖競爭。只有當某執行緒嘗試獲取鎖的時候,發現該鎖已經被占用,只能等待其釋放,這才發生了鎖競爭。
在輕量級鎖狀態下繼續鎖競爭,沒有搶到鎖的執行緒將自旋,即不停地迴圈判斷鎖是否能夠被成功獲取。獲取鎖的操作,其實就是通過cas修改物件頭里的鎖標誌位。先比較當前鎖標誌位是否為「釋放」,如果是則將其設定為「鎖定」,比較並設定是原子性發生的。這就算搶到鎖了,然後執行緒將當前鎖的持有者資訊修改為自己。
長時間的自旋操作是非常消耗資源的,乙個執行緒持有鎖,其他執行緒就只能在原地空耗cpu,執行不了任何有效的任務,這種現象叫做忙等(busy-waiting)。如果多個執行緒用乙個鎖,但是沒有發生鎖競爭,或者發生了很輕微的鎖競爭,那麼synchronized就用輕量級鎖,允許短時間的忙等現象。這是一種折衷的想法,短時間的忙等,換取執行緒在使用者態和核心態之間切換的開銷。
顯然,此忙等是有限度的(有個計數器記錄自旋次數,預設允許迴圈10次,可以通過虛擬機器引數更改)。如果鎖競爭情況嚴重,某個達到最大自旋次數的執行緒,會將輕量級鎖公升級為重量級鎖(依然是cas修改鎖標誌位,但不修改持有鎖的執行緒id)。當後續執行緒嘗試獲取鎖時,發現被占用的鎖是重量級鎖,則直接將自己掛起(而不是忙等),等待將來被喚醒。在jdk1.6之前,synchronized直接加重量級鎖,很明顯現在得到了很好的優化。
乙個鎖只能按照 偏向鎖、輕量級鎖、重量級鎖的順序逐漸公升級(也有叫鎖膨脹的),不允許降級。
偏向鎖 輕量級鎖 重量級鎖
首先簡單說下先偏向鎖 輕量級鎖 重量級鎖三者各自的應用場景 偏向鎖 只有乙個執行緒進入臨界區 輕量級鎖 多個執行緒交替進入臨界區 重量級鎖 多個執行緒同時進入臨界區。還要明確的是,偏向鎖 輕量級鎖都是jvm引入的鎖優化手段,目的是降低執行緒同步的開銷。比如以下的同步 塊 synchronized l...
輕量級鎖,偏向鎖,重量級鎖
物件頭資訊 使用monitor物件來實現重量級鎖,如果使用重量級鎖,加鎖過程就需要先去關聯monitor物件,然後還需要各種判斷。asychronized關鍵字實現重量級鎖的原理 monitorenter和monitorexit就是操作monitor物件,會有效能損耗,所以引入輕量級鎖。當出現重量級...
偏向鎖,輕量級鎖,重量級鎖(java)
輕量級鎖是jdk 1.6之中加入的新型鎖機制,它名字中的 輕量級 是相對於使用作業系統互斥量來實現的傳統鎖而言的,因此傳統的鎖機制就稱為 重量級 鎖。首先需要強調一點的是,輕量級鎖並不是用來代替重量級鎖的,它的本意是在沒有多執行緒競爭的前提下,減少傳統的重量級鎖使用作業系統互斥量產生的效能消耗。要理...