大家知道,Java的多線程安全是基于Lock機制實現的,而Lock的性能往往不如人意。
原因是,monitorenter與monitorexit這兩個控制多線程同步的bytecode原語,是JVM依賴操作系統互斥(mutex)來實現的。
互斥是一種會導致線程掛起,并在較短的時間內又需要重新調度回原線程的,較為消耗資源的操作。
為了優化Java的Lock機制,從Java6開始引入了輕量級鎖的概念。
輕量級鎖(Lightweight Locking)本意是為了減少多線程進入互斥的幾率,并不是要替代互斥。
它利用了CPU原語Compare-And-Swap(CAS,匯編指令CMPXCHG),嘗試在進入互斥前,進行補救。
本文將詳細介紹JVM如何利用CAS,實現輕量級鎖。
原理詳解
Java Object Model中定義,Object Header是一個2字(1 word = 4 byte)長度的存儲區域。
第一個字長度的區域用來標記同步,GC以及hash code等,官方稱之為 mark word。第二個字長度的區域是指向到對象的Class。
在2個word中,mark word是輕量級鎖實現的關鍵。它的結構見下表

從表中可以看到,state為lightweight locked的那行即為輕量級鎖標記。bitfieds名為指向lock record的指針,這里的lock record,其實是一塊分配在線程堆棧上的空間區域。
用于CAS前,拷貝object上的mark word(為什么要拷貝,請看下文)。
第三項是重量級鎖標記。后面的狀態單詞很有趣,inflated,譯為膨脹,在這里意思其實是鎖已升級到OS-level。
在本文的范圍內,我們只關注第二和第三項即可。
為了能直觀的理解lock,unlock與mark word之間的聯系,我畫了一張流程圖:

在圖中,提到了拷貝object mark word,由于脫離了原始mark word,官方將它冠以displaced前綴,即displaced mark word(置換標記字)。
這個displaced mark word是整個輕量級鎖實現的關鍵,在CAS中的compare就需要用它作為條件。
為什么要拷貝mark word?
其實很簡單,原因是為了不想在lock與unlock這種底層操作上再加同步。
在拷貝完object mark word之后,JVM做了一步交換指針的操作,即流程中第一個橙色矩形框內容所述。
將object mark word里的輕量級鎖指針指向lock record所在的stack指針,作用是讓其他線程知道,該object monitor已被占用。
lock record里的owner指針指向object mark word的作用是為了在接下里的運行過程中,識別哪個對象被鎖住了。
下圖直觀地描述了交換指針的操作。


最后一步unlock中,我們發現,JVM同樣使用了CAS來驗證object mark word在持有鎖到釋放鎖之間,有無被其他線程訪問。
如果其他線程在持有鎖這段時間里,嘗試獲取過鎖,則可能自身被掛起,而mark word的重量級鎖指針也會被相應修改。
此時,unlock后就需要喚醒被掛起的線程。
原文鏈接:http://kenwublog.com/theory-of-lightweight-locking-upon-cas