Java 加鎖與解鎖

今天看極客時間的時候看到了 java 鎖的一篇文章,其中關於加鎖與解鎖的幾個點不是特別理解,晚上專門差了一下資料,算是弄明白了。java

疑問

爲何解鎖的時候,cas 釋放失敗就證實有其餘線程來獲取過鎖呢?安全

原理

每個java對象都擁有一個對象頭,對象頭分爲2個部分:Mark Word(標記字段) 與 Class Pointer(類型指針)。jvm

  • Mark Word用於存儲對象自身的運行時數據,自身運行時的數據:哈希碼(HashCode)、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程 ID、偏向時間戳等,它是實現輕量級鎖和偏向鎖的關鍵。
  • Class Pointer是對象指向它的類元數據的指針,虛擬機經過這個指針來肯定這個對象是哪一個類的實例。

32位虛擬機的Mark Word以下:
Mark Word可能存儲4種數據post

分析

先說一下鎖的種類:偏向鎖,輕量級鎖,重量級鎖。spa


偏向鎖

獲取流程:

  1. 確認對象是可偏向狀態(是否偏向:1,鎖標緻:01);
  2. 可偏向,查詢 Mark Word 中 線程ID是否爲當前線程ID,是則進行5,不是則3;
  3. 未指向當前線程,則 CAS 嘗試獲取鎖,獲取成功,將 Mark Word 中線程ID更換,執行5,不然執行4;
  4. 若是獲取失敗,則說明有線程競爭,那麼在 safe-point 安全點時,獲取鎖的線程掛起,升級爲輕量級鎖,而後被阻塞在安全點的線程繼續執行後面的代碼;
  5. 執行代碼。

釋放流程:

不主動釋放,只有當出現競爭,進入到安全點時纔會觸發釋放邏輯,這也是3爲何會獲取成功的緣由。
結果是要麼釋放被其餘線程獲取,要麼升級輕量級鎖。線程


輕量級鎖

獲取流程:

  1. 線程進入同步代碼塊時,若是是偏向鎖的狀態;線程在本身的棧幀空間內創建一個 Mark Record 的空間,用於存儲 Mark Word 的備份,官方稱爲 Displaced Marl Word,這是線程與對象頭的狀態是這樣的:
  2. 拷貝 Mark Word 到 Lock Record 中;
  3. CAS 嘗試將對象頭的 Mark Word 指向 Lock Record,並將 Lock Record 中的 owner 執行 mark word,若是成功執行4,不然5;
  4. 若是更新成功,就證實獲得鎖,並將鎖標緻位更新爲00,表示對象處於輕量級鎖,此時線程與對象頭的狀態:
  5. 若是更新失敗,jvm 先判斷 mark word 是否指向當前棧幀,若是是表明線程已經獲取鎖,進入重入狀態;若是不是證實已經被其餘線程獲取,自旋等待必定時間,若是仍未獲取,升級爲重量級鎖(將 mark work 從指向線程指針改成指向互斥量)。

釋放流程

  1. 經過 CAS 將線程中複製的 Displaced Mark Work 替換會原先的 Mark Word;
  2. 替換失敗,說明有其餘線程嘗試過獲取該鎖(此時鎖已膨脹),那就要在釋放鎖的同時,喚醒被掛起的線程。

這裏說一下輕量級鎖釋放失敗是就證實鎖升級的原理,由於以前 mark word 指向的是本線程的指針,這個是 cas 指望的值,可是被其餘線程更改成了指向互斥量的對象了,cas 就失敗,就證實升級爲了重量級鎖。指針

參考文檔

https://www.itqiankun.com/art...
https://juejin.im/post/5b4eec...code

相關文章
相關標籤/搜索