日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Monitor对象是什么?

發布時間:2023/12/18 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Monitor对象是什么? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

synchronized保證線程同步的作用相信大家都已經非常熟悉了,可以把任意一個對象當作鎖。synchronized 關鍵字無論是修飾代碼塊,還是修飾實例方法和靜態方法,本質上都是作用于對象上。

多個線程要競爭共享資源,而操作共享資源資源的代碼就在臨界區內,想要進入到這個臨界區就必須持有鎖。

當用 synchronized 修飾代碼塊時,編譯后的字節碼會有 monitorentermonitorexit 指令,分別對應的是獲得鎖和解鎖。

當用 synchronized 修飾方法時,會給方法加上標記 ACC_SYNCHRONIZED,這樣 JVM 就知道這個方法是一個同步方法,于是在進入同步方法的時候就會進行執行競爭鎖的操作,只有拿到鎖才能繼續執行。

對象鎖長啥樣?

那么對象鎖在內存中是怎樣的呢?接下來就來看一下對象鎖的實現細節。對象鎖的狀態是記錄在對象頭中的Mark word區域中。關于對象的內存區域的細節,大家可以參考前文《Java面試必考問題:對象在內存中是如何布局的? 》。

在不同的鎖狀態下,Mark word會存儲不同的信息,這也是為了節約內存常用的設計。當鎖狀態為重量級鎖(鎖標識位=10)時,Mark word中會記錄指向Monitor對象的指針,這個Monitor對象也稱為管程或監視器鎖

每個對象都存在著一個 Monitor對象與之關聯。執行 monitorenter 指令就是線程試圖去獲取 Monitor 的所有權,搶到了就是成功獲取鎖了;執行 monitorexit 指令則是釋放了Monitor的所有權

ObjectMonitor類

在HotSpot虛擬機中,Monitor是基于C++ObjectMonitor類實現的,其主要成員包括:

  • _owner:指向持有ObjectMonitor對象的線程
  • _WaitSet:存放處于wait狀態的線程隊列,即調用wait()方法的線程
  • _EntryList:存放處于等待鎖block狀態的線程隊列
  • _count:約為_WaitSet 和 _EntryList 的節點數之和
  • _cxq: 多個線程爭搶鎖,會先存入這個單向鏈表
  • _recursions: 記錄重入次數

上圖簡略展示了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::enter() 和 ObjectMonitor::exit() 分別是ObjectMonitor獲取鎖和釋放鎖的方法。線程解鎖后還會喚醒之前等待的線程,根據策略選擇直接喚醒_cxq隊列中的頭部線程去競爭,或者將_cxq隊列中的線程加入_EntryList,然后再喚醒_EntryList隊列中的線程去競爭。

ObjectMonitor::enter()

下面我們看一下ObjectMonitor::enter()方法競爭鎖的流程:

首先嘗試通過 CAS 把 ObjectMonitor 中的 _owner 設置為當前線程,設置成功就表示獲取鎖成功。通過 _recursions 的自增來表示重入。

如果沒有CAS成功,那么就開始啟動自適應自旋,自旋還不行的話,就包裝成 ObjectWaiter 對象加入到 _cxq 單向鏈表之中。關于自旋鎖和自適應自旋,可以參考前文《Java面試必考問題:什么是自旋鎖 》](https://www.toutiao.com/i6934327407897854475/?group_id=6934327407897854475)。

加入_cxq鏈表后,再次嘗試是否可以CAS拿到鎖,再次失敗就要阻塞(block),底層調用了pthread_mutex_lock

ObjectMonitor::exit()方法

線程執行 Object.wait()方法時,會將當前線程加入到 _waitSet 這個雙向鏈表中,然后再運行ObjectMonitor::exit() 方法來釋放鎖。

可重入鎖就是根據 _recursions 來判斷的,重入一次就執行 _recursions++,解鎖一次就執行 _recursions–,如果 _recursions 減到 0 ,就說明需要釋放鎖了。

線程解鎖后還會喚醒之前等待的線程。當線程執行 Object.notify()方法時,從 _waitSet 頭部拿線程節點,然后根據策略(QMode指定)決定將線程節點放在哪里,包括_cxq 或 _EntryList 的頭部或者尾部,然后喚醒隊列中的線程。

總結

以上是生活随笔為你收集整理的Monitor对象是什么?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。