Java鎖詳解
鎖的狀態總共有四種:無鎖狀態、偏向鎖狀態、輕量級鎖狀態和重量級鎖狀態。這幾個狀態會隨著競爭情況逐漸升級。為了提高獲得鎖和釋放鎖的效率,鎖可以升級但不能降級。
偏向鎖
為了在無多執行緒競爭的情況下減少執行緒獲得鎖的代價,引入了偏向鎖的概念。
偏向鎖只需要在切換執行緒ID的時候進行CAS操作,之後該執行緒在進入和退出同步塊的時候不再需要進行CAS操作來加鎖與解鎖。因此偏向鎖可以提高單一執行緒多次操作同步塊的效能。
當執行緒B要使用一個同步物件的時候,會查詢物件頭裡的Mark Word的鎖狀態是否為可偏向狀態。如果是,測試執行緒ID是否指向本執行緒。
- 如果是,則表示執行緒B已經獲得鎖了,執行同步程式碼即可。
- 如果不是,則通過CAS操作來競爭偏向鎖(首先會暫停擁有偏向鎖的執行緒A,檢查持有偏向鎖的執行緒A是否活著,如果執行緒A不處於活動狀態,則競爭成功;如果執行緒A仍然存活,則表明存在競爭關係,競爭失敗。)
- 如果競爭成功,則將Mark Word設定為無鎖狀態,然後將Mark Word重新設定偏向鎖,Thread ID指向執行緒B;
- 如果競爭失敗,則表明有競爭關係。當到達全域性安全點的時候,獲得偏向鎖的程式A會被掛起,偏向鎖會升級為輕量級鎖,然後被掛起的程式A會繼續向下執行,程式B通過自旋競爭鎖。
偏向鎖只有當有其他執行緒競爭鎖的時候才會釋放,偏向鎖的撤銷會在全域性安全點的時候進行。
輕量級鎖
執行緒執行同步塊之前,會在當前執行緒的棧幀中建立用於儲存鎖記錄的空間,並將物件頭的Mark Word複製到鎖記錄中,官方稱為Displaced Mark Word。執行緒通過CAS嘗試將物件頭中的Mark Word替換為指向鎖記錄的指標。如果成功,執行緒獲得鎖;如果失敗,表示有其他執行緒競爭鎖了,當前執行緒會嘗試使用自旋來獲得鎖。
如果自旋失敗,輕量級鎖會膨脹為重量級鎖。
輕量級解鎖時,會使用CAS操作將Displaced替換回到物件頭。如果成功則表示沒有競爭關係,如果失敗(失敗的原因是Mark Word的鎖標誌位被修改導致CAS失敗),表示當前鎖存在競爭關係,且鎖已經被升級為重量級鎖。
重量級鎖
重量級鎖下,當執行緒試圖獲取鎖的時候,執行緒會被阻塞,當持有鎖的執行緒釋放鎖之後會喚醒這些執行緒,被喚醒的執行緒之間會進行鎖的爭奪。
鎖狀態之間的轉換概括
如果多個執行緒不存在競爭同步塊的問題,則物件會保持偏向鎖的狀態。當出現兩個執行緒同時競爭同步塊的時候,鎖會升級為輕量級鎖,後來的執行緒會通過自旋等待擁有鎖的程式執行完同步程式碼。如果成功則將Mark Word替換成指向棧中鎖記錄的指標。如果失敗多次則表示有其他執行緒競爭鎖,則膨脹為重量級鎖。
CAS操作
CAS非常重要,理解了CAS的概念才能知道上面的操作成功失敗的真正意義。
CAS原理:CAS有三個運算元,即記憶體值v,舊的運算元a,新的運算元b。當我們需要更新v對應的記憶體地址中的值為b時,首先判斷記憶體中的值是否和我們之前的所見值a相同,若相同則將v對應的記憶體地址儲存的值修改為b,若不同,則什麼都不做。
也可以理解舊運算元為期望值,當期望值與對應記憶體中的值相同時,視為滿足期望。此時才將b值寫入記憶體值v對應的記憶體。
鎖的優缺點對比
鎖 | 優點 | 缺點 | 適用場景 |
---|---|---|---|
偏向鎖 | 加鎖和解鎖不需要額外的消耗,和執行非同步方法比僅存在納秒級的差距。 | 如果執行緒間存在鎖競爭,會帶來額外的鎖撤銷的消耗。 | 適用於只有一個執行緒訪問同步塊場景 |
輕量級鎖 | 競爭的執行緒不會阻塞,提高了程式的響應速度。 | 如果始終得不到鎖競爭的執行緒使用自旋會消耗CPU。 | 追求響應時間。同步塊執行速度非常快。 |
重量級鎖 | 執行緒競爭不使用自旋,不會消耗CPU。 | 執行緒阻塞,響應時間緩慢。 | 追求吞吐量。同步塊執行速度較長。 |
相關文章
- 例項詳解 Java 死鎖與破解死鎖Java
- 02-Java中的鎖詳解Java
- Java中的鎖原理、鎖優化、CAS、AQS詳解!Java優化AQS
- Java鎖最全詳解:樂觀鎖/悲觀鎖+公平鎖/非公平鎖+獨享鎖/共享鎖Java
- java併發程式設計 | 鎖詳解:AQS,Lock,ReentrantLock,ReentrantReadWriteLockJava程式設計AQSReentrantLock
- Java併發程式設計中的鎖機制詳解Java程式設計
- Java 讀寫鎖 ReadWriteLock 原理與應用場景詳解Java
- MySQL鎖詳解!(轉載)MySql
- 詳解Redis分散式鎖Redis分散式
- Java中的讀寫鎖ReentrantReadWriteLock詳解,存在一個小缺陷Java
- JAVA多執行緒詳解(3)執行緒同步和鎖Java執行緒
- MySQL資料庫-鎖詳解MySql資料庫
- MySQL 悲觀鎖與樂觀鎖的詳解MySql
- 一文搞懂MySQL行鎖、表鎖、間隙鎖詳解MySql
- Java註解詳解Java
- Java 註解詳解Java
- Golang 讀寫鎖RWMutex 互斥鎖Mutex 原始碼詳解GolangMutex原始碼
- ReentrantLock可重入鎖——原始碼詳解ReentrantLock原始碼
- 資料庫篇:mysql鎖詳解資料庫MySql
- Java String 詳解Java
- Java 反射詳解Java反射
- 詳解 Java NIOJava
- java方法詳解Java
- java反射詳解Java反射
- Java Stream 詳解Java
- 【Java】JDBC詳解JavaJDBC
- Java ThreadPoolExecutor詳解Javathread
- Java SPI詳解Java
- Java註解(Annotation)詳解Java
- 意向共享鎖與意向排它鎖:詳解與應用
- 高併發程式設計系列:4種Java執行緒鎖全面詳解程式設計Java執行緒
- Java併發包原始碼學習系列:ReentrantLock可重入獨佔鎖詳解Java原始碼ReentrantLock
- Java註解最全詳解(超級詳細)Java
- 詳解Java鎖的升級與對比(1)——鎖的分類與細節(結合部分原始碼)Java原始碼
- 詳解 MySql InnoDB 中意向鎖的作用MySql
- MySQL共享鎖:使用與例項詳解MySql
- 詳解 MySql InnoDB 中的三種行鎖(記錄鎖、間隙鎖與臨鍵鎖)MySql
- Java 物件排序詳解Java物件排序