1. 程式人生 > >看完你就明白的鎖系列之鎖的狀態

看完你就明白的鎖系列之鎖的狀態

前面兩篇文章我介紹了一下

  • 看完你就應該能明白的悲觀鎖和樂觀鎖
  • 看完你就明白的鎖系列之自旋鎖

看完你就會知道,執行緒如果鎖住了某個資源,致使其他執行緒無法訪問的這種鎖被稱為悲觀鎖,相反,執行緒不鎖住資源的鎖被稱為樂觀鎖,而自旋鎖是基於 CAS 機制實現的,CAS又是樂觀鎖的一種實現,那麼對於鎖來說,多個執行緒同步訪問某個資源的流程細節是否一樣呢?換句話說,在多執行緒同步訪問某個資源時,鎖的狀態會如何變化呢?本篇文章來探討一下。

鎖狀態的分類

Java 語言專門針對 synchronized 關鍵字設定了四種狀態,它們分別是:無鎖、偏向鎖、輕量級鎖和重量級鎖,但是在瞭解這些鎖之前還需要先了解一下 Java 物件頭和 Monitor。

Java 物件頭

我們知道 synchronized 是悲觀鎖,在操作同步之前需要給資源加鎖,這把鎖就是物件頭裡面的,而Java 物件頭又是什麼呢?我們以 Hotspot 虛擬機器為例,Hopspot 物件頭主要包括兩部分資料:Mark Word(標記欄位)Klass Pointer(型別指標)

Mark Word:預設儲存物件的HashCode,分代年齡和鎖標誌位資訊。這些資訊都是與物件自身定義無關的資料,所以Mark Word被設計成一個非固定的資料結構以便在極小的空間記憶體儲存儘量多的資料。它會根據物件的狀態複用自己的儲存空間,也就是說在執行期間Mark Word裡儲存的資料會隨著鎖標誌位的變化而變化。

Klass Point:物件指向它的類元資料的指標,虛擬機器通過這個指標來確定這個物件是哪個類的例項。

在32位虛擬機器和64位虛擬機器的 Mark Word 所佔用的位元組大小不一樣,32位虛擬機器的 Mark Word 和 Klass Pointer 分別佔用 32bits 的位元組,而 64位虛擬機器的 Mark Word 和 Klass Pointer 佔用了64bits 的位元組,下面我們以 32位虛擬機器為例,來看一下其 Mark Word 的位元組具體是如何分配的

用中文翻譯過來就是

  • 無狀態也就是無鎖的時候,物件頭開闢 25bit 的空間用來儲存物件的 hashcode ,4bit 用於存放分代年齡,1bit 用來存放是否偏向鎖的標識位,2bit 用來存放鎖標識位為01
  • 偏向鎖 中劃分更細,還是開闢25bit 的空間,其中23bit 用來存放執行緒ID,2bit 用來存放 epoch,4bit 存放分代年齡,1bit 存放是否偏向鎖標識, 0表示無鎖,1表示偏向鎖,鎖的標識位還是01
  • 輕量級鎖中直接開闢 30bit 的空間存放指向棧中鎖記錄的指標,2bit 存放鎖的標誌位,其標誌位為00
  • 重量級鎖中和輕量級鎖一樣,30bit 的空間用來存放指向重量級鎖的指標,2bit 存放鎖的標識位,為11
  • GC標記開闢30bit 的記憶體空間卻沒有佔用,2bit 空間存放鎖標誌位為11。

其中無鎖和偏向鎖的鎖標誌位都是01,只是在前面的1bit區分了這是無鎖狀態還是偏向鎖狀態。

關於為什麼這麼分配的記憶體,我們可以從 OpenJDK 中的markOop.hpp類中的列舉窺出端倪

來解釋一下

  • age_bits 就是我們說的分代回收的標識,佔用4位元組
  • lock_bits 是鎖的標誌位,佔用2個位元組
  • biased_lock_bits 是是否偏向鎖的標識,佔用1個位元組
  • max_hash_bits 是針對無鎖計算的hashcode 佔用位元組數量,如果是32位虛擬機器,就是 32 - 4 - 2 -1 = 25 byte,如果是64 位虛擬機器,64 - 4 - 2 - 1 = 57 byte,但是會有 25 位元組未使用,所以64位的 hashcode 佔用 31 byte
  • hash_bits 是針對 64 位虛擬機器來說,如果最大位元組數大於 31,則取31,否則取真實的位元組數
  • cms_bits 我覺得應該是不是64位虛擬機器就佔用 0 byte,是64位就佔用 1byte
  • epoch_bits 就是 epoch 所佔用的位元組大小,2位元組。

Synchronized鎖

synchronized用的鎖是存在Java物件頭裡的。

JVM基於進入和退出 Monitor 物件來實現方法同步和程式碼塊同步。程式碼塊同步是使用 monitorenter 和 monitorexit 指令實現的,monitorenter 指令是在編譯後插入到同步程式碼塊的開始位置,而 monitorexit 是插入到方法結束處和異常處。任何物件都有一個 monitor 與之關聯,當且一個 monitor 被持有後,它將處於鎖定狀態。

根據虛擬機器規範的要求,在執行 monitorenter 指令時,首先要去嘗試獲取物件的鎖,如果這個物件沒被鎖定,或者當前執行緒已經擁有了那個物件的鎖,把鎖的計數器加1,相應地,在執行 monitorexit 指令時會將鎖計數器減1,當計數器被減到0時,鎖就釋放了。如果獲取物件鎖失敗了,那當前執行緒就要阻塞等待,直到物件鎖被另一個執行緒釋放為止。

Monitor

Synchronized是通過物件內部的一個叫做監視器鎖(monitor)來實現的,監視器鎖本質又是依賴於底層的作業系統的 Mutex Lock(互斥鎖)來實現的。而作業系統實現執行緒之間的切換需要從使用者態轉換到核心態,這個成本非常高,狀態之間的轉換需要相對比較長的時間,這就是為什麼 Synchronized 效率低的原因。因此,這種依賴於作業系統 Mutex Lock 所實現的鎖我們稱之為重量級鎖

Java SE 1.6為了減少獲得鎖和釋放鎖帶來的效能消耗,引入了偏向鎖輕量級鎖:鎖一共有4種狀態,級別從低到高依次是:無鎖狀態、偏向鎖狀態、輕量級鎖狀態和重量級鎖狀態。鎖可以升級但不能降級。

所以鎖的狀態總共有四種:無鎖狀態、偏向鎖、輕量級鎖和重量級鎖。隨著鎖的競爭,鎖可以從偏向鎖升級到輕量級鎖,再升級的重量級鎖(但是鎖的升級是單向的,也就是說只能從低到高升級,不會出現鎖的降級)。JDK 1.6中預設是開啟偏向鎖和輕量級鎖的,我們也可以通過-XX:-UseBiasedLocking=false來禁用偏向鎖。

鎖的分類及其解釋

無鎖

無鎖狀態,無鎖即沒有對資源進行鎖定,所有的執行緒都可以對同一個資源進行訪問,但是隻有一個執行緒能夠成功修改資源。

無鎖的特點就是在迴圈內進行修改操作,執行緒會不斷的嘗試修改共享資源,直到能夠成功修改資源並退出,在此過程中沒有出現衝突的發生,這很像我們在之前文章中介紹的 CAS 實現,CAS 的原理和應用就是無鎖的實現。無鎖無法全面代替有鎖,但無鎖在某些場合下的效能是非常高的。

偏向鎖

Hotspot 的作者經過研究發現,大多數情況下,鎖不僅不存在多執行緒競爭,還存在鎖由同一執行緒多次獲得的情況,偏向鎖就是在這種情況下出現的,它的出現是為了解決只有在一個執行緒執行同步時提高效能。

可以從物件頭的分配中看到,偏向鎖要比無鎖多了執行緒IDepoch,當一個執行緒訪問同步程式碼塊並獲取鎖時,會在物件頭和棧幀的記錄中儲存執行緒的ID,等到下一次執行緒在進入和退出同步程式碼塊時就不需要進行 CAS 操作進行加鎖和解鎖,只需要簡單判斷一下物件頭的 Mark Word 中是否儲存著指向當前執行緒的執行緒ID,判斷的標誌當然是根據鎖的標誌位來判斷的。

偏向鎖的獲取過程

  1. 訪問 Mark Word 中偏向鎖的標誌是否設定成 1,鎖的標誌位是否是 01 --- 確認為可偏向狀態。

  2. 如果確認為可偏向狀態,判斷當前執行緒id 和 物件頭中儲存的執行緒 ID 是否一致,如果一致的話,則執行步驟5,如果不一致,進入步驟3
  3. 如果當前執行緒ID 與物件頭中儲存的執行緒ID 不一致的話,則通過 CAS 操作來競爭獲取鎖。如果競爭成功,則將 Mark Word 中的執行緒ID 修改為當前執行緒ID,然後執行步驟5,如果不一致,則執行步驟4
  4. 如果 CAS 獲取偏向鎖失敗,則表示有競爭(CAS 獲取偏向鎖失敗則表明至少有其他執行緒曾經獲取過偏向鎖,因為執行緒不會主動釋放偏向鎖)。當到達全域性安全點(SafePoint)時,會首先暫停擁有偏向鎖的執行緒,然後檢查持有偏向鎖的執行緒是否存活(因為可能持有偏向鎖的執行緒已經執行完畢,但是該執行緒並不會主動去釋放偏向鎖),如果執行緒不處於活動狀態,則將物件頭置為無鎖狀態(標誌位為01),然後重新偏向新的執行緒;如果執行緒仍然活著,撤銷偏向鎖後升級到輕量級鎖的狀態(標誌位為00),此時輕量級鎖由原持有偏向鎖的執行緒持有,繼續執行其同步程式碼,而正在競爭的執行緒會進入自旋等待獲得該輕量級鎖。
  5. 執行同步程式碼

偏向鎖的釋放過程

偏向鎖的釋放過程可以參考上述的步驟4 ,偏向鎖在遇到其他執行緒競爭鎖時,持有偏向鎖的執行緒才會釋放鎖,執行緒不會主動釋放偏向鎖。偏向鎖的撤銷,需要等待全域性安全點(在這個時間點上沒有位元組碼正在執行),它會首先暫停擁有偏向鎖的執行緒,判斷鎖是否處於被鎖定狀態,撤銷偏向鎖後恢復到未鎖定(標誌位為01)或輕量級鎖(標誌位為00)的狀態。

關閉偏向鎖

偏向鎖在Java 6 和Java 7 裡是預設啟用的。由於偏向鎖是為了在只有一個執行緒執行同步塊時提高效能,如果你確定應用程式裡所有的鎖通常情況下處於競爭狀態,可以通過JVM引數關閉偏向鎖:-XX:-UseBiasedLocking=false,那麼程式預設會進入輕量級鎖狀態。

關於 epoch

真正理解 epoch 的概念比較複雜,這裡簡單理解,就是 epoch 的值可以作為一種檢測偏向鎖有效性的時間戳

輕量級鎖

輕量級鎖是指當前鎖是偏向鎖的時候,被另外的執行緒所訪問,那麼偏向鎖就會升級為輕量級鎖,其他執行緒會通過自旋的形式嘗試獲取鎖,不會阻塞,從而提高效能。

加鎖過程

在程式碼進入同步塊的時候,如果同步物件鎖狀態為無鎖狀態(鎖標誌位為 01 狀態,是否為偏向鎖為 0 ),虛擬機器首先將在當前執行緒的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用於儲存鎖物件目前的 Mark Word 的拷貝,然後拷貝物件頭中的 Mark Word 複製到鎖記錄中。

拷貝成功後,虛擬機器將使用 CAS 操作嘗試將物件的 Mark Word 更新為指向 Lock Record 的指標,並將 Lock Record裡的 owner 指標指向物件的 Mark Word。

如果這個更新動作成功了,那麼這個執行緒就擁有了該物件的鎖,並且物件Mark Word的鎖標誌位設定為 00 ,表示此物件處於輕量級鎖定狀態。

如果這個更新操作失敗了,虛擬機器首先會檢查物件的 Mark Word 是否指向當前執行緒的棧幀,如果是就說明當前執行緒已經擁有了這個物件的鎖,那就可以直接進入同步塊繼續執行。否則說明多個執行緒競爭鎖,輕量級鎖就要膨脹為重量級鎖,鎖標誌的狀態值變為 10 ,Mark Word中儲存的就是指向重量級鎖(互斥量)的指標,後面等待鎖的執行緒也要進入阻塞狀態。

重量級鎖

重量級鎖也就是通常說 synchronized 的物件鎖,鎖標識位為10,其中指標指向的是 monitor 物件(也稱為管程或監視器鎖)的起始地址。每個物件都存在著一個 monitor 與之關聯,物件與其 monitor 之間的關係有存在多種實現方式,如 monitor 可以與物件一起建立銷燬或當執行緒試圖獲取物件鎖時自動生成,但當一個 monitor 被某個執行緒持有後,它便處於鎖定狀態。

上圖簡單描述多執行緒獲取鎖的過程,當多個執行緒同時訪問一段同步程式碼時,首先會進入 Entry Set當執行緒獲取到物件的 monitor 後進入 The Owner 區域並把 monitor 中的 owner 變數設定為當前執行緒,同時 monitor 中的計數器count 加1,若執行緒呼叫 wait() 方法,將釋放當前持有的 monitor,owner變數恢復為 null,count自減1,同時該執行緒進入 WaitSet 集合中等待被喚醒。若當前執行緒執行完畢也將釋放 monitor (鎖)並復位變數的值,以便其他執行緒進入獲取monitor(鎖)。

由此看來,monitor 物件存在於每個Java物件的物件頭中(儲存的指標的指向),synchronized 鎖便是通過這種方式獲取鎖的,也是為什麼Java中任意物件可以作為鎖的原因,同時也是 notify/notifyAll/wait 等方法存在於頂級物件Object中的原因。(部分來源於網路)

文章參考:

不可不說的Java“鎖”事

白話 Synchronized

https://gist.github.com/arturmkrtchyan/43d6135e8a15798cc46c

https://blog.csdn.net/zhoufanyang_china/article/details/54601311

Synchronized鎖效能優化偏向鎖輕量級鎖升級 多執行緒中篇(五)

https://juejin.im/post/5bfe6ddee51d45491b0163eb

https://zhuanlan.zhihu.com/p/29866981

http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.94.8487&rep=rep1&type=pdf

https://blogs.oracle.com/dave/biased-locking-in-hotspot

[java 偏向鎖、輕量級鎖及重量級鎖synchronized原理](

下面為自己做個宣傳,歡迎關注公眾號 Java建設者,號主是Java技術棧,熱愛技術,喜歡閱讀,熱衷於分享和總結,希望能把每一篇好文章分享給成長道路上的你。關注公眾號回覆 002 領取為你特意準備的大禮包,你一定會喜歡並收藏的。

相關推薦

明白系列自旋

在上一篇文章 看完你就應該能明白的悲觀鎖和樂觀鎖中我們已經學習到了什麼是悲觀鎖和樂觀鎖、悲觀鎖和樂觀鎖的實現、優缺點分別是什麼。其中樂觀鎖的實現之一 CAS 演算法中提到了一個自旋鎖的概念,為了全面理解 CAS 演算法就首先需要了解一下自旋鎖 是什麼,自旋鎖的適用場景和優缺點分別是什麼,彆著急,下面為你一一列

明白系列狀態

前面兩篇文章我介紹了一下 看完你就應該能明白的悲觀鎖和樂觀鎖 看完你就明白的鎖系列之自旋鎖 看完你就會知道,執行緒如果鎖住了某個資源,致使其他執行緒無法訪問的這種鎖被稱為悲觀鎖,相反,執行緒不鎖住資源的鎖被稱為樂觀鎖,而自旋鎖是基於 CAS 機制實現的,CAS又是樂觀鎖的一種實現,那麼對於鎖來說,多個執行

科目三直線行駛怎麽才能不跑偏?明白

轉動 img load 超過 alt span str spa src 一、視線看遠點 二、直線行駛時只能微修方向盤,最好不超過5度 三、換檔時,左右拿捏方向盤搖晃,穩不住,導致跑偏 直線行駛步驟分解 1. 直線行駛前,應把方向調正,調到車道線的中間;擋位進入3擋,車速在

電腦錄屏桌面哪個軟體好?明白

    首先一說到電腦桌面錄屏這個東西,我知道大家都不陌生,但是我想說的是你們在用過這麼多的軟體之後有什麼認為好用的錄屏軟體嗎?之前就會有寶寶們問我關於這方面的知識,但是由於小編沒有自己親身試驗過所以不敢輕易向大家推薦,今天寫這邊文章的目的就是來回答之前寶寶們的疑問的,如果感興趣的寶寶

erp系統為什麼要選穩定性強的?明白

隨著社會生產力的發展,企業對資訊化管理需求也迅速增強,如此形勢之下,紛紛開啟了尋求erp管理系統的旅程,面對市場上大量湧現的ERP軟體,企業該如何選擇呢?下面小編就為大家分享一些選擇ERP系統的建議和方法,希望對大家能有所幫助。選擇ERP管理系統需考慮三大要素: 一、首先要立足自身業務,隨著行業的不斷細分,

erp系統為什麽要選穩定性強的?明白

之一 企業 發揮 最有 努力 如何選擇 erp系統 過程 快速 隨著社會生產力的發展,企業對信息化管理需求也迅速增強,如此形勢之下,紛紛開啟了尋求erp管理系統的旅程,面對市場上大量湧現的ERP軟件,企業該如何選擇呢?下面小編就為大家分享一些選擇ERP系統的建議和方法,希望

手機裏語音轉文字怎麽實現的呢?明白

com 流程 主頁 一起 中文 選擇 過程 圖片 src 無論那個行業,都有開會、聽課、采訪的時候。當領導講話太快,我們跟不上節奏之時,就會采取錄音的方式來重播摘錄要點。但是這樣的方法效率太低,不僅費時,且準確率也不高,我在這裏教一個效率高的語音轉文字方法給大家,我們一起看

知道的樂觀和悲觀

目錄 Java 鎖之樂觀鎖和悲觀鎖 悲觀鎖 樂觀鎖 兩種鎖的使用場景 樂觀鎖的實現方式 版本號機制 CAS 演算法 樂觀鎖的缺

【轉載】知道什麽是HTTPS了

答案 賬號 不同 傳輸層安全 被人 交互 .html 中間人 非對稱加密 什麽是 HTTPS ? 不管是使用手機還是電腦上網,都離不開數據的通訊 現在互聯網上傳輸數據,普遍使用的是超文本傳輸協議,即 HTTP (HyperText Transfer Protocol) 所以

vintage、遷移率、滾動率、入催率等概念——懂了(轉載)

聯網 edit ews 問題 目的 content 時間 客戶 clear 隨著互聯網金融的發展,對數據分析的需求越來越大。數據分析的目的其實是為了找到風險和收益的平衡點。高收益伴隨著高風險,而低風險的回報又如同雞肋。所以,太高的風險,太低的收益都不行

還沉浸在大公司就是螺絲釘小公司鍛鍊人?懂了

剛畢業那會經歷過很多所謂創業公司,和很多朋友經歷過畫大餅,洗腦以及公司上市原始股這樣的承諾。當你正在趟過這些謊言你就會發現,在這個世界上能信這些鬼話的也只有涉世未深的畢業生了。小公司裡真的就是十幾二十幾個精英帶你一路向前?沒有辦公室政治?呵呵,金庸說過有人的地方就有江湖。在經濟下滑的今天小公

學Java培訓,要英語基礎嗎?明白

有人問這樣的問題:英語成績差可以學Java嗎?英語不好影響Java學習嗎?到睿峰諮詢Java課程的同學也常常有這樣的擔憂,經常有人問到成都Java培訓機構學習要英語基礎嗎?今天就告訴大家答案。 到成都Java培訓機構學習要英語基礎嗎?答案是肯定的,每個人的心裡也是明白的,畢竟所有的程式設計程式碼都是由英語單

C++和java該怎麼選?哪門語言的前景好呢?知道了!

現在肯定有很多小夥伴在學習程式設計,那麼那些在學習程式設計的小夥伴有時候會很疑惑C++和java這兩個程式設計界的巨頭該怎麼選擇呢?學習哪門語言比較好嗯? 今天小編就簡單的的來聊聊這兩門語言吧!關於這兩門的語言的簡介小編就偷個懶百度有的,直接搜尋下就行了。 C++和java有什麼區別呢?

團隊在做什麼事?知道了

開發、營銷和合作夥伴更新 您好OneLedger社群, 通過本期簡報,我們希望為您提供有關以下部門的最新資訊:開發,營銷和社群以及合作伙伴關係。 我們釋出了OneLedger協議的v0.6.1版本。對於此版本,我們重建了底層序列化程式碼,以支援鏈和事務的多型處

隨手記投資安全嗎?知道了

之前做過一個調查很多人都想了解關於隨手記和隨手投資的介紹今天就來分享啦! 在深圳南山的科技園坐落了許多的高新技術企業 我們也在這裡 高新南十二路的金蝶軟體園 就是隨手記所在地從2010年誕生至今隨手記已經八歲了一起回顧下隨手記的成長之路 2010年-誕生

會正則表示式了

最近看了一篇關於正則表示式的學習筆記,覺得講的非常好,更有圖形化的神器相助,想不學會都難,所以想轉給大家看看。 話說不是開發為啥要學正則表示式這種看似很晦澀的東西呢,因為現在很多搜尋的場景都是支援正則表示式的,學會了正則表示式就有如一把利劍在手。 本文較長,建議

完全可以去工作了,誰敢不要!?

1、自動篩選  2、字元替換  3、表裡編輯巨集  4、表中匯入外部資料  5、行列快速轉換  6、在表中執行巨集  7、在表中新增巨集  8、文字與字母的分列合併  9、加多個檢視  10、設定表中列印區域  11、表中建立圖表 12、獲取網路資料  13、表中搜索

初學者如何從零開始學習人工智慧?懂了

此文是想要進入人工智慧這個領域、但不知道從哪裡開始的初學者最佳的學習資源列表。   一、機器學習   有關機器學習領域的最佳介紹,請觀看Coursera的Andrew Ng機器學習課程。 它解釋了基本概念,並讓你很好地理解最重要的演算法。  

初學者如何從零學習人工智慧?懂了

此文是想要進入人工智慧這個領域、但不知道從哪裡開始的初學者最佳的學習資源列表。 一、機器學習 有關機器學習領域的最佳介紹,請觀看Coursera的Andrew Ng機器學習課程。 它解釋了基本概念,並讓你很好地理解最重要的演算法。 “Programming Coll

知道什麼是 HTTPS 了------其實我發現很多人不懂https, 包括我!所以來學下這篇佳作!

什麼是 HTTPS ? 不管是使用手機還是電腦上網,都離不開資料的通訊 現在網際網路上傳輸資料,普遍使用的是超文字傳輸協議,即 HTTP (HyperText Transfer Protocol) 所以,我們以前在上網的時候,會發現所有的網址都一個 http:// 字首: HTTP 協議 簡單而言,HTT