你們知道,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後就須要喚醒被掛起的線程。安全