併發編程離不開鎖,然而每次遇到鎖問題時都會談鎖色變,下面對鎖實現做簡單化描述,方便你們容易理解!java
在JAVA中鎖一共有四種狀態:無鎖狀態、偏向鎖狀態、輕量級鎖狀態和重量級鎖狀態(按從低到高順序,鎖着競爭狀況逐漸升級)算法
JAVA中鎖只能升級卻不能降級,目的是爲了提升得到鎖和釋放鎖的效率。 編程
在HopSpot虛擬機中,對象在內存存儲中分爲3部分:對象頭(Header)、實例數據(Instance Data) 和 對齊填充(Padding)。 想了解Java對象結構的詳細信息請看:java對象結構安全
java對象頭包含3部分信息,以下: 多線程
鎖的狀態保存在對象頭的Mark Word中,以32位JDK爲例: 併發
在無多線程競爭鎖的狀況下,爲了讓同一線程得到鎖的代價更低而引入了偏向鎖。學習
CAS是一種無鎖算法,CAS有3個操做數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改成B,不然什麼都不作。 測試
偏向鎖使用了一種等到競爭出現才釋放鎖的機制,因此當其餘線程嘗試競爭偏向鎖時,持有偏向鎖的線程纔會釋放鎖。 偏向鎖的撤銷,須要等待全局安全點(在這個時間點上沒有正在執行的字節碼),它會先暫停擁有偏向鎖的線程,判斷鎖對象是否處於被鎖定狀態,撤銷偏向鎖後恢復到未鎖定(標誌位爲「01」)或輕量級鎖(標誌位爲「00」)的狀態。優化
線程在執行同步塊以前,JVM會先在當前線程的棧幀中建立用於存儲鎖記錄的空間,並將對象頭中的Mark Word複製到鎖記錄(Lock record)中,官方成爲Displaced Mark Word。而後嘗試使用CAS算法將對象頭中的Mark Word替換爲指向鎖記錄的指針。若是成功,當前線程得到鎖,若是失敗,表示其餘線程競爭鎖,當前線程便嘗試使用自旋來獲取鎖。操作系統
輕量級鎖解鎖時,會使用原子的CAS操做將Displaced Mark Word替換回對象頭,若是成功,則表示沒有競爭發生。若是失敗,表示當前存在競爭,鎖就會膨脹成重量級鎖。
由於自旋會消耗CPU,爲了不無用的自旋(好比得到鎖的線程被阻塞了),一旦鎖升級成重量級鎖,就不會再恢復到輕量級鎖狀態。其餘線程試圖獲取鎖時,會被阻塞住,當持有鎖的線程釋放以後會喚醒這些線程,被喚醒的線程就會進行新一輪的奪鎖競爭。
在多線程併發編程中synchronized一直是元老級角色,不少人稱呼它爲「重量級鎖」。synchronized是經過對象內部的一個叫作監視器鎖(monitor)來實現的,可是監視器鎖本質又是依賴於底層操做系統的Mutex Lock來實現的,而操做系統實現線程之間的切換就須要從用戶狀態切換到核心狀態,這個成本很高,狀態之間轉換須要相對比較長的時間,這就是synchronized效率低的緣由。所以,這種依賴於操做系統Mutex Lock來實現的鎖,咱們稱之爲「重量級鎖」。
在JDK1.6後,synchronized獲得了種種優化後,在某些狀況下已經沒那麼重了。
注:本文主要觀點來源於《java併發編程的藝術》和我的學習網上的文章的一些總結。