java monitor是什么意思,Java面试常见问题:Monitor对象是什么?
synchronized保證線程同步的作用相信大家都已經非常熟悉了,可以把任意一個對象當作鎖。synchronized 關鍵字無論是修飾代碼塊,還是修飾實例方法和靜態方法,本質上都是作用于對象上。
多個線程要競爭共享資源,而操作共享資源資源的代碼就在臨界區內,想要進入到這個臨界區就必須持有鎖。
當用 synchronized 修飾代碼塊時,編譯后的字節碼會有 monitorenter 和 monitorexit 指令,分別對應的是獲得鎖和解鎖。
當用 synchronized 修飾方法時,會給方法加上標記 ACC_SYNCHRONIZED,這樣 JVM 就知道這個方法是一個同步方法,于是在進入同步方法的時候就會進行執行競爭鎖的操作,只有拿到鎖才能繼續執行。
對象鎖長啥樣?
那么對象鎖在內存中是怎樣的呢?接下來就來看一下對象鎖的實現細節。對象鎖的狀態是記錄在對象頭中的Mark word區域中。關于對象的內存區域的細節,大家可以參考前文《Java面試必考問題:對象在內存中是如何布局的? 》。
對象內存布局
在不同的鎖狀態下,Mark word會存儲不同的信息,這也是為了節約內存常用的設計。當鎖狀態為重量級鎖(鎖標識位=10)時,Mark word中會記錄指向Monitor對象的指針,這個Monitor對象也稱為管程或監視器鎖。
64 位時 MarkWord 在不同狀態下的內存布局
每個對象都存在著一個 Monitor對象與之關聯。執行 monitorenter 指令就是線程試圖去獲取 Monitor 的所有權,搶到了就是成功獲取鎖了;執行 monitorexit 指令則是釋放了Monitor的所有權。
ObjectMonitor類
在HotSpot虛擬機中,Monitor是基于C++的ObjectMonitor類實現的,其主要成員包括:
_owner:指向持有ObjectMonitor對象的線程
_WaitSet:存放處于wait狀態的線程隊列,即調用wait()方法的線程
_EntryList:存放處于等待鎖block狀態的線程隊列
_count:約為_WaitSet 和 _EntryList 的節點數之和
_cxq: 多個線程爭搶鎖,會先存入這個單向鏈表
_recursions: 記錄重入次數
ObjectMonitor的基本工作機制
上圖簡略展示了ObjectMonitor的基本工作機制:
(1)當多個線程同時訪問一段同步代碼時,首先會進入 _EntryList 隊列中。
(2)當某個線程獲取到對象的Monitor后進入臨界區域,并把Monitor中的 _owner 變量設置為當前線程,同時Monitor中的計數器 _count 加1。即獲得對象鎖。
(3)若持有Monitor的線程調用 wait() 方法,將釋放當前持有的Monitor,_owner變量恢復為null,_count自減1,同時該線程進入 _WaitSet 集合中等待被喚醒。
(4)在_WaitSet 集合中的線程會被再次放到_EntryList 隊列中,重新競爭獲取鎖。
(5)若當前線程執行完畢也將釋放Monitor并復位變量的值,以便其他線程進入獲取鎖。
線程爭搶鎖的過程要比上面展示得更加復雜。除了_EntryList 這個雙向鏈表用來保存競爭的線程,ObjectMonitor中還有另外一個單向鏈表 _cxq,由兩個隊列來共同管理并發的線程。
ObjectMonitor的并發管理邏輯
ObjectMonitor::enter() 和 ObjectMonitor::exit() 分別是ObjectMonitor獲取鎖和釋放鎖的方法。線程解鎖后還會喚醒之前等待的線程,根據策略選擇直接喚醒_cxq隊列中的頭部線程去競爭,或者將_cxq隊列中的線程加入_EntryList,然后再喚醒_EntryList隊列中的線程去競爭。
ObjectMonitor::enter()
ObjectMonitor::enter()競爭鎖的流程
下面我們看一下ObjectMonitor::enter()方法競爭鎖的流程:
首先嘗試通過 CAS 把 ObjectMonitor 中的 _owner 設置為當前線程,設置成功就表示獲取鎖成功。通過 _recursions 的自增來表示重入。
如果沒有CAS成功,那么就開始啟動自適應自旋,自旋還不行的話,就包裝成 ObjectWaiter 對象加入到 _cxq 單向鏈表之中。關于自旋鎖和自適應自旋,可以參考前文《Java面試必考問題:什么是自旋鎖 》。
加入_cxq鏈表后,再次嘗試是否可以CAS拿到鎖,再次失敗就要阻塞(block),底層調用了pthread_mutex_lock。
ObjectMonitor::exit()方法
線程執行 Object.wait()方法時,會將當前線程加入到 _waitSet 這個雙向鏈表中,然后再運行ObjectMonitor::exit() 方法來釋放鎖。
可重入鎖就是根據 _recursions 來判斷的,重入一次就執行 _recursions++,解鎖一次就執行 _recursions--,如果 _recursions 減到 0 ,就說明需要釋放鎖了。
線程解鎖后還會喚醒之前等待的線程。當線程執行 Object.notify()方法時,從 _waitSet 頭部拿線程節點,然后根據策略(QMode指定)決定將線程節點放在哪里,包括_cxq 或 _EntryList 的頭部或者尾部,然后喚醒隊列中的線程。
我會持續更新關于物聯網、云原生以及數字科技方面的文章,用簡單的語言描述復雜的技術,也會偶爾發表一下對IT產業的看法,歡迎大家關注,謝謝。
總結
以上是生活随笔為你收集整理的java monitor是什么意思,Java面试常见问题:Monitor对象是什么?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: QuickBooks qbo api t
- 下一篇: 美光科技任命高云松担任大中华区政府事务副