synchronized的鎖機制的主要優點是Java語言內置的鎖機制,所以,JVM能夠自由的優化而不影響已存在的代碼。java
任何對象都擁有對象頭這一數據結構來支持鎖,可是對於較大的對象系統開銷會更大一些。安全
java中的每個對象都至少包含2個字(24 Bytes for 32bits & 28 Bytes for 64bits, 不包括已壓縮的對象)。第一個字被稱爲Mark Word。這是一個對象的頭,它包含了不一樣的信息,包括鎖的相關信息。
第二個字是指向metadata class的指針,metadata class字義了對象的類型。這部分也包含了VMT(Virtual Method Table)。數據結構
Mark Word 的結構以下所示:優化
Mark Word根據最低兩位(Tag)的所表示的狀態,編碼了不一樣的信息。
若是這個對象沒有被用做鎖,Mark Word 記錄了hashcode和對象年齡(for GC/survivors)。
除此以外,有3種狀態對應鎖:輕量級鎖,重量級鎖和偏向鎖。編碼
全部現代JVM都引入了經量級鎖:spa
避免將每一個對象關聯操做系統的mutex/condition變量(重量級鎖)操作系統
當不存在鎖競爭時,使用原子操做來進入退出同步塊線程
若是發生鎖競爭,回退到操做系統的重量級鎖指針
引入輕量級鎖會提供鎖效率,由於大部分鎖都不存在競爭。code
經量級鎖的加鎖過程:
當一個對象被鎖定時,mark word被複制到當前嘗試獲取鎖的線程的線程棧(Execution stack)的鎖記錄空間(lock record), 被複制的mark word官方稱爲displaced mark。
使用CAS操做來嘗試使 mark word指向當前線程的鎖記錄空間(即在mark word中存入使用當前線程鎖記錄空間的指針——stack pointer)。
若是CAS操做成功,則線程得到鎖。
若是CAS操做失敗,即代表存在鎖競爭,則發生鎖膨脹,回退到重量級鎖。
鎖記錄空間中記錄了被當前執行方法鎖定的對象(經過遍歷線程棧找到線程的鎖對象)
經量級鎖加鎖前:
經量級鎖加鎖後:
經量級鎖的解鎖過程:
解鎖使用CAS來把displaced mark寫回對象的mark word中。
若是CAS失敗, 表示發生鎖競爭:則鎖膨脹。(通知其餘等待線程鎖已釋放)
將鎖記錄空間置爲0:若是發生鎖膨脹,則用0替換displaced mardk,若是不存在競爭,則CAS將鎖記錄空間置爲0後,中止CAS操做。
偏向鎖的引入:
在多處理器上CAS操做可能開銷很大。
大多數鎖不只不存在競爭,並且每每由同一個線程使用。
使單獨一個線程獲取鎖的開銷更低。
代價是使另外一個線程獲取鎖開銷增大。
偏向鎖加鎖過程:
當鎖對象第一次被線程獲取時,VM把對象頭中的標誌位設爲101,即偏向模式。同時使用CAS把獲取到這個鎖的線程ID記錄在對象的mark word中,若是CAS成功,則持有偏向鎖的線程之後每次進行這個鎖相關的同步塊時,再也不進行任務同步操做,只進行比較Mark word中的線程ID是不是當前線程的ID。
偏向鎖的解鎖過程:
當另一個線程去嘗試獲取這個鎖時,偏向模式結束。根據鎖對象目前是否處於被鎖定狀態,撤銷偏向後恢復到未鎖定或經量級鎖定狀態。
VM會中止持有偏向鎖的線程(實際上,VM不能中止單一線程,而是在安全點進行的操做)。
遍歷持有偏向鎖的線程的棧,找到鎖記錄空間,將displaced mark 寫入到最舊的鎖記錄空間,其餘的寫0。
更新鎖對象的mark word。若是被鎖定,則指向最舊的鎖記錄空間,不然,填入未鎖定值。
偏向鎖的特色:
偏向於第一個獲取鎖的線程:
在mark word的Tag中增長一位
001表示無鎖狀態
101表示偏向或可偏向狀態(thread ID ==0 == unlock)
經過CAS來獲取偏向鎖
對於持有鎖的線程接下的鎖獲取和釋放開銷很是小(僅僅判斷下,不須要CAS同步操做)。
若是另外一個線程鎖定了偏向鎖對象,則偏向鎖收回,升級爲輕量級鎖(增長了另外一個線程獲取鎖的開銷)。