【併發程式設計】(二)Java併發機制底層實現原理——synchronized關鍵字
synchronized定義
synchronized
Java語言的關鍵字,可用來給物件和方法或者程式碼塊加鎖,當它鎖定一個方法或者一個程式碼塊的時候,同一時刻最多隻有一個執行緒執行這段程式碼。
當兩個併發執行緒訪問同一個物件object中的這個加鎖同步程式碼塊時,一個時間內只能有一個執行緒得到執行。另一個執行緒必須等待當前執行緒執行完這個程式碼塊以後才能執行該程式碼塊。
然而,當一個執行緒訪問object的一個加鎖程式碼塊時,另一個執行緒仍可以訪問該object中的非加鎖程式碼塊。
- 在【併發程式設計】(一)Java併發機制的底層實現原理——volatile關鍵字中,介紹了JMM:java記憶體模型。
JMM中最重要的問題:重排序、原子性、可見性。而volatile、synchronized、final、和Lock都可以保證可見性:
- volatile:通過禁止違背Happends-before原則的重排序操作,和變數改變時立即重新整理值到主存,來保證。
- synchronized和lock:都是通過把併發的過程強制轉化為原子化的過程;
- final:變數本來就是放在多個執行緒共享的區域,自然可見;
synchronized實現原理
物件頭
在HotSpot虛擬機器中,java物件在虛擬機器中的儲存佈局分為3塊區域:物件頭、例項資料和對齊填充;而synchronized用的鎖就是存在java物件頭裡的。
- java物件頭:
長度 | 內容 | 說明 |
---|---|---|
32/64bit | Mark Word | 儲存物件的hashcode或鎖資訊 |
32/64bit | Class Metadata Address | 儲存物件型別資料的指標 |
32/64bit | Array Length | 陣列的長度(如果當前物件是陣列) |
- 32位的JVM的Mark Word中的預設儲存結構無鎖狀態如下:
鎖狀態 | 25bit | 4bit | 1bit是否是偏向鎖 | 2bit鎖標誌位 |
---|---|---|---|---|
無鎖狀態 | 物件的hashcode | 物件分代年齡 | 0 | 01 |
- 在執行期間,Mark word中儲存的資料會隨著鎖標誌位的變化而變化:
鎖狀態 | 25bit | 4bit | 1bit是否是偏向鎖 | 2bit鎖標誌位 |
---|---|---|---|---|
偏向鎖 | 執行緒ID I Epoch | 物件分代年齡 | 1 | 01 |
鎖狀態 | 25bit I 4bit I 1bit是否是偏向鎖 | 2bit鎖標誌位 |
---|---|---|
輕量級鎖 | 指向棧中鎖記錄的指標 | 00 |
鎖狀態 | 25bit I 4bit I 1bit是否是偏向鎖 | 2bit鎖標誌位 |
---|---|---|
重量級鎖 | 指向重量級鎖的指標 | 10 |
鎖分類
Java SE 1.6為了減少獲取鎖和釋放鎖帶來的效能消耗,引入了偏向鎖和輕量級鎖。在Java SE 1.6中鎖一共有4種狀態:
- 無鎖 ——> 偏向鎖 ——> 輕量級鎖 ——> 重量級鎖
這幾個狀態會隨著競爭情況逐漸升級,鎖可以升級但不能降級。
1. 偏向鎖
HotSpot的作者發現,大多數情況下,鎖不僅不存在多執行緒競爭,而且總是由同一執行緒多次獲得;當一個執行緒訪問同步塊並獲取鎖時,會在物件頭和棧幀中的鎖記錄裡儲存鎖偏向的執行緒id。
以後該執行緒在進入和退出同步塊時不需要進行CAS來加鎖和解鎖;只需要簡單地測試一下物件頭中是否儲存著指向當前執行緒的偏向鎖。
- 偏向鎖的撤銷:當其他執行緒嘗試競爭偏向鎖時,持有偏向鎖的執行緒才會釋放。
- 偏向鎖的優點:當只有一個執行緒執行同步塊時,不需要CAS操作直接獲取鎖,提升效能;
- 適用於一個執行緒反覆獲取同一個鎖的情況。
2. 輕量級鎖
輕量級鎖,也叫自旋鎖:
執行緒在執行同步塊之前,JVM會在當前的 棧幀 中建立Lock Record用於儲存鎖記錄的空間,將物件頭中的Mark Word複製到Lock Record,官方稱為Displaced Mark Word。
執行緒嘗試使用CAS將物件頭中的Mark Word替換為 指向鎖記錄的指標 。
- 如果成功,當前執行緒獲取鎖;
- 如果失敗,表示其他執行緒競爭鎖,當前執行緒嘗試使用 自旋 來獲取鎖。
輕量鎖的解鎖:使用原子的CAS將Displaced Mark Word替換回到物件頭:
- 如果成功,表示沒有競爭發生;
- 如果失敗,表示當前鎖存在競爭,鎖就會膨脹為重量級鎖。
3. 重量級鎖
升級為重量級鎖後,Mark Word中儲存的是指向重量級鎖的指標,此時等待鎖的執行緒都會進入到 阻塞 狀態。
Synchronzed的重量級鎖是通過物件內部的一個監視器鎖 Monitor 來實現的。而Monitor又是依賴於底層作業系統的Mutex Lock互斥鎖實現的。而作業系統實現執行緒之間的切換需要從使用者態轉為核心態,成本很高。
- Monitor:同一個時刻,只有一個程式或者執行緒可以進入monitor中定義的臨界區,這使得monitor可以達到互斥的效果。而無法進入臨界區的程式或執行緒,它們應該被阻塞,並且在必要的時候被喚醒。
- Monitor需要的元素:
- 臨界區
- monitor物件及鎖
- 條件變數及定義在monitor物件上的wait、signal操作
在Java語言中,我們知道使用synchronized關鍵字時,總是需要指定一個物件與之關聯,比如this或者this.Class物件。這個物件就是 Monitor Object ,即java.lang.Object。Object物件提供了wait()、notify()等方法對monitor機制進行了封裝。
- Java Monitor原理:
- 當執行緒需要獲取一個物件的鎖時,會被放入EntrySet進行等待;
- 此執行緒獲取了鎖,成為鎖的擁有者,進入到The Owner區域;
- 擁有者執行緒可以呼叫wait()釋放鎖,進入WaitSet進入等待狀態;
- 其他執行緒呼叫notify()喚醒等待集合中的執行緒
鎖升級過程
- 當前沒有多執行緒的競爭,只有一個執行緒去獲取鎖, 進入偏向鎖模式 (在物件頭中儲存執行緒ID)
- 另一個執行緒來爭取鎖:判斷物件是否為偏向狀態(此時為是)、判斷物件頭中儲存的執行緒id是否為本執行緒id(此時不是):那麼嘗試通過CAS設定為當前執行緒ID
- 成功(因為上一個持有偏向鎖的執行緒是不會主動釋放的),獲取偏向鎖並執行程式碼塊;
- 失敗(表示有執行緒競爭的情況存在),撤銷偏向鎖 模式,準備升級輕量級鎖:
- 在棧幀中開闢一片鎖記錄空間,用於儲存當前物件Mark Word的拷貝;
- 使用CAS操作嘗試把物件的Mark Word更新為指向此執行緒的棧幀鎖記錄指標。
CAS成功,表示成功獲取鎖:將 鎖模式設定為輕量級鎖
CAS失敗,採用 自旋 方式不斷重試
- 自旋當超出一定次數時,表示競爭激烈, 直接升級為重量級鎖 。在此狀態下,所有等待的執行緒都進入到阻塞狀態,而不再CAS重試。
相關文章
- 【Java併發程式設計】Synchronized關鍵字實現原理Java程式設計synchronized
- 《java併發程式設計的藝術》併發底層實現原理Java程式設計
- 併發程式設計原理學習:synchronized關鍵字程式設計synchronized
- Java併發程式設計:Synchronized及其實現原理Java程式設計synchronized
- [Java併發系列] 1.Java併發機制的底層實現Java
- 併發程式設計——synchronized關鍵字的使用程式設計synchronized
- Java併發——關鍵字synchronized解析Javasynchronized
- 併發機制的底層實現
- 【讀書筆記】Java併發機制的底層實現原理筆記Java
- Java 併發機制底層實現 —— volatile 原理、synchronize 鎖優化機制Java優化
- Java 多執行緒併發程式設計之 Synchronized 關鍵字Java執行緒程式設計synchronized
- Java高併發之synchronized關鍵字Javasynchronized
- 併發程式設計基礎底層原理學習(二)程式設計
- Java併發程式設計:synchronizedJava程式設計synchronized
- Java併發程式設計volatile關鍵字Java程式設計
- java併發程式設計——volatile關鍵字Java程式設計
- java併發程式設計:volatile關鍵字Java程式設計
- Java併發程式設計序列之JUC底層AQS(二)Java程式設計AQS
- java學習——併發專題——synchronized關鍵字Javasynchronized
- 併發系列之「Java中的synchronized關鍵字」Javasynchronized
- Java併發程式設計之synchronizedJava程式設計synchronized
- Java併發程式設計-synchronized指南Java程式設計synchronized
- Java併發程式設計:volatile關鍵字解析Java程式設計
- Java併發程式設計:Synchronized底層優化(偏向鎖、輕量級鎖)Java程式設計synchronized優化
- 併發程式設計的鎖機制:synchronized和lock程式設計synchronized
- Java併發程式設計序列之執行緒間通訊-synchronized關鍵字-volatile關鍵字Java程式設計執行緒synchronized
- 併發程式設計面試必備:synchronized 關鍵字使用、底層原理、JDK1.6 之後的底層優化以及 和ReenTrantLock 的對比程式設計面試synchronizedJDK優化ReentrantLock
- 併發程式設計 synchronized程式設計synchronized
- 併發程式設計之ThreadLocal、Volatile、synchronized、Atomic關鍵字程式設計threadsynchronized
- Java之併發程式設計:volatile關鍵字解析Java程式設計
- Java併發程式設計序列之JUC底層AQSJava程式設計AQS
- 併發程式設計基礎底層原理學習(一)程式設計
- 併發程式設計基礎底層原理學習(四)程式設計
- 併發程式設計之:synchronized程式設計synchronized
- 再識Java併發程式設計關鍵字之volatileJava程式設計
- 【Java併發程式設計】synchronized相關面試題總結Java程式設計synchronized面試題
- Java併發容器,底層原理深入分析Java
- Java併發程式設計——為什麼要用volatile關鍵字Java程式設計