1. 同步的原理
JVM規(guī)范規(guī)定JVM基于進入和退出Monitor對象來實現(xiàn)方法同步和代碼塊同步,但兩者的實現(xiàn)細節(jié)不一樣。代碼塊同步是使用monitorenter和monitorexit指令實現(xiàn),而方法同步是使用另外一種方式實現(xiàn)的,細節(jié)在JVM規(guī)范里并沒有詳細說明,但是方法的同步同樣可以使用這兩個指令來實現(xiàn)。monitorenter指令是在編譯后插入到同步代碼塊的開始位置,而monitorexit是插入到方法結束處和異常處, JVM要保證每個monitorenter必須有對應的monitorexit與之配對。任何對象都有一個 monitor 與之關聯(lián),當且一個monitor 被持有后,它將處于鎖定狀態(tài)。線程執(zhí)行到 monitorenter 指令時,將會嘗試獲取對象所對應的 monitor 的所有權,即嘗試獲得對象的鎖。
2. Java對象頭
鎖存在Java對象頭里。如果對象是數(shù)組類型,則虛擬機用3個Word(字寬)存儲對象頭,如果對象是非數(shù)組類型,則用2字寬存儲對象頭。在32位虛擬機中,一字寬等于四字節(jié),即32bit。
長度內容說明32/64bitMark Word存儲對象的hashCode或鎖信息等32/64bitClass Metadata Address存儲到對象類型數(shù)據(jù)的指針32/64bitArray length數(shù)組的長度(如果當前對象是數(shù)組)Java對象頭里的Mark Word里默認存儲對象的HashCode,分代年齡和鎖標記位。32位JVM的Mark Word的默認存儲結構如下:
25 bit4bit1bit
是否是偏向鎖
2bit
鎖標志位
無鎖狀態(tài)對象的hashCode對象分代年齡001在運行期間Mark Word里存儲的數(shù)據(jù)會隨著鎖標志位的變化而變化。Mark Word可能變化為存儲以下4種數(shù)據(jù):

3. 幾種鎖的類型
線程的阻塞和喚醒需要CPU從用戶態(tài)轉為核心態(tài),頻繁的阻塞和喚醒對CPU來說是一件負擔很重的工作。
Java SE1.6為了減少獲得鎖和釋放鎖所帶來的性能消耗,引入了“偏向鎖”和“輕量級鎖”,所以在Java SE1.6里鎖一共有四種狀態(tài),無鎖狀態(tài),偏向鎖狀態(tài),輕量級鎖狀態(tài)和重量級鎖狀態(tài),它會隨著競爭情況逐漸升級。
鎖可以升級但不能降級,意味著偏向鎖升級成輕量級鎖后不能降級成偏向鎖。這種鎖升級卻不能降級的策略,目的是為了提高獲得鎖和釋放鎖的效率。
3.1 偏向鎖
Hotspot的作者經(jīng)過以往的研究發(fā)現(xiàn)大多數(shù)情況下鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得。偏向鎖的目的是在某個線程獲得鎖之后,消除這個線程鎖重入(CAS)的開銷,看起來讓這個線程得到了偏護。

偏向鎖的進一步理解
偏向鎖的釋放不需要做任何事情,這也就意味著加過偏向鎖的MarkValue會一直保留偏向鎖的狀態(tài),因此即便同一個線程持續(xù)不斷地加鎖解鎖,也是沒有開銷的。
另一方面,偏向鎖比輕量鎖更容易被終結,輕量鎖是在有鎖競爭出現(xiàn)時升級為重量鎖,而一般偏向鎖是在有不同線程申請鎖時升級為輕量鎖,這也就意味著假如一個對象先被線程1加鎖解鎖,再被線程2加鎖解鎖,這過程中沒有鎖沖突,也一樣會發(fā)生偏向鎖失效,不同的是這回要先退化為無鎖的狀態(tài),再加輕量鎖,如圖:

另外,JVM對那種會有多線程加鎖,但不存在鎖競爭的情況也做了優(yōu)化,聽起來比較拗口,但在現(xiàn)實應用中確實是可能出現(xiàn)這種情況,因為線程之前除了互斥之外也可能發(fā)生同步關系,被同步的兩個線程(一前一后)對共享對象鎖的競爭很可能是沒有沖突的。對這種情況,JVM用一個epoch表示一個偏向鎖的時間戳(真實地生成一個時間戳代價還是蠻大的,因此這里應當理解為一種類似時間戳的identifier),對epoch,官方是這么解釋的:
A similar mechanism, called bulk rebiasing, optimizes situations in which objects of a class are locked and unlocked by different threads but never concurrently. It invalidates the bias of all instances of a class without disabling biased locking. An epoch value in the class acts as a timestamp that indicates the validity of the bias. This value is copied into the header word upon object allocation. Bulk rebiasing can then efficiently be implemented as an increment of the epoch in the appropriate class. The next time an instance of this class is going to be locked, the code detects a different value in the header word and rebiases the object towards the current thread.