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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

在c#中用mutex类实现线程的互斥_面试官经常问的synchronized实现原理和锁升级过程,你真的了解吗...

發布時間:2024/7/5 C# 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在c#中用mutex类实现线程的互斥_面试官经常问的synchronized实现原理和锁升级过程,你真的了解吗... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本篇文章主要從字節碼和JVM底層來分析synchronized實現原理和鎖升級過程,其中涉及到了簡單認識字節碼、對象內部結構以及ObjectMonitor等知識點。

閱讀本文之前,如果大家對synchronized關鍵字的基本使用還不是很了解的話,推薦閱讀筆者之前的一遍關于synchronized關鍵字使用的文章:

synchronized三種使用方式都不知道還想通過面試,門都沒有

從字節碼角度分析synchronized實現

從JVM規范中可以了解到,無論是synchronized修飾方法(實例/靜態方法)還是代碼塊都是基于進入(entry)和退出(exit)monitor對象來實現,但是兩種修飾方式在字節碼層面實現上有著很大區別。下面我們通過javap -verbose XXX.class命令查看class文件信息來具體分析兩者實現上的差異。

synchronized修飾代碼塊:

程序源碼如下:

源碼截圖

class文件信息如下:

class文件信息截圖

由上面的class信息可以得知,使用synchronized修飾代碼塊會在同步代碼塊之前加monitorenter指令,同時在代碼塊正常退出(15行)和異常退出(21行)的地方插入monitorexit指令,從而保證monitorenter和monitorexit的成對執行(保證同步代碼塊執行結束的同時釋放鎖資源)。可以把monitorenter看作lock.lock(),monitorexit看作lock.unlock(),那么monitorenter和monitorexit可以用更加方便理解的偽代碼表示,如下:

偽代碼截圖

synchronized修飾方法:

程序源碼如下:

源碼截圖

class文件信息如下:

class文件信息截圖

由上面的class信息可以得知,synchronized修飾方法并沒有通過插入monitorentry和monitorexit指令來實現,而是在方法表結構中的訪問標志(access_flags)設置ACC_SYNCHRONIZED標志來實現。線程在執行方法前先判斷access_flags是否標記ACC_SYNCHRONIZED,如果標記則在執行方法前先去獲取monitor對象,獲取成功則執行方法代碼且執行完畢后釋放monitor對象,獲取失敗則表示monitor對象被其他線程獲取從而阻塞當前線程。

對象頭和MarkWord

說到對象頭,我們需要先整體了解下對象的內部結構,如下圖所示:

對象內部結構圖

由圖可知對象內部結構分為:對象頭、實例數據、對齊填充(保證8個字節的倍數)。對象頭分為對象標記(markOop)和類元信息(klassOop)。類元信息存儲的是指向該對象類元數據(klass)的首地址,4個字節。

對象標記(markOop)是我們重要要介紹的,它存儲對象本身運行時的數據,如哈希碼、GC標記、鎖信息、線程關聯等(64位JVM占8個字節,32位JVM占4個字節),稱為"Mark Word",存儲格式非規定與具體JVM實現有關。

Hotspot JVM中MarkWord存儲格式如下:

32位存儲格式:

32位存儲格式

64位存儲格式:

64位存儲格式

由MarkWord存儲格式可以了解到JVM可以通過鎖標志位來判斷鎖類型,進而進行處理。注意JDK1.6之前只有重量級鎖的,JDK1.6之后才有了偏向鎖和輕量級鎖,后面鎖升級部分會詳細講解。

ObjectMonitor

在JVM的規范中,有這么一些話:“在JVM中,每個對象和類在邏輯上都是和一個監視器相關聯的,為了實現監視器的排他性監視能力,JVM為每一個對象和類都關聯一個鎖,鎖住了一個對象,就是獲得對象相關聯的監視器”。這里的監視器就是指的是ObjectMonitor。

ObjectMonitor在JVM源碼中的定義如下:

ObjectMonitor的JVM源碼

MarkWord中重量級鎖指向的重量級指針就是ObjectMonitor對象指針,是基于操作系統互斥(mutex)實現的。

synchronized鎖升級和實現原理

synchronized在修飾方法和代碼塊在字節碼上實現方式有很大差異,但是內部實現還是基于對象頭的MarkWord來實現的。JDK1.6之前synchronized使用的是重量級鎖,JDK1.6之后進行了優化,擁有了無鎖->偏向鎖->輕量級鎖->重量級鎖的升級過程,而不是無論什么情況都使用重量級鎖

synchronized涉及的鎖歸類

鎖升級的優化是針對于不同同步場景進行的優化,在不存在鎖競爭的時候進入同步方法/代碼塊則使用偏向鎖,存在競爭時升級為輕量級鎖,輕量級鎖采用的是自旋鎖,如果同步方法/代碼塊執行時間很短的話,采用輕量級鎖雖然會占用cpu資源但是相對比使用重量級鎖還是更高效的,但是如果同步方法/代碼塊執行時間很長,那么使用輕量級鎖自旋帶來的性能消耗就比使用重量級鎖更嚴重,這時候就需要升級為重量級鎖。

MarkWord結構和鎖特征

下面結合上圖所示的MarkWord對幾種鎖類型進行介紹:

  • 無鎖:MarkWord標志位01,沒有線程執行同步方法/代碼塊時的狀態。
  • 偏向鎖:MarkWord標志位01(和無鎖標志位一樣)。偏向鎖是通過在bitfields中通過CAS設置當前正在執行的ThreadID來實現的。假設線程A獲取偏向鎖執行代碼塊(即對象頭設置了ThreadA_ID),線程A同步塊未執行結束時,線程B通過CAS嘗試設置ThreadB_ID會失敗,因為存在鎖競爭情況,這時候就需要升級為輕量級鎖。注:偏向鎖是針對于不存在資源搶占情況時候使用的鎖,如果被synchronized修飾的方法/代碼塊競爭線程多可以通過禁用偏向鎖來減少一步鎖升級過程。可以通過JVM參數-XX:-UseBiasedLocking = false來關閉偏向鎖。
  • 輕量級鎖:MarkWord標志位00。輕量級鎖是采用自旋鎖的方式來實現的,自旋鎖分為固定次數自旋鎖和自適應自旋鎖。 輕量級鎖是針對競爭鎖對象線程不多且線程持有鎖時間不長的場景, 因為阻塞線程需要CPU從用戶態轉到內核態,代價很大,如果一個剛剛阻塞不久就被釋放代價有大。具體實現和升級為重量級鎖過程:線程A獲取輕量級鎖時會把對象頭中的MarkWord復制一份到線程A的棧幀中創建用于存儲鎖記錄的空間DisplacedMarkWord,然后使用CAS將對象頭中的內容替換成線程A存儲DisplacedMarkWord的地址。如果這時候出現線程B來獲取鎖,線程B也跟線程A同樣復制對象頭的MarkWord到自己的DisplacedMarkWord中,如果線程A鎖還沒釋放,這時候那么線程B的CAS操作會失敗,會繼續自旋,當然不可能讓線程B一直自旋下去,自旋到一定次數(固定次數/自適應)就會升級為重量級鎖。
  • 重量級鎖:通過對象內部監視器(monitor)實現,monitor本質前面也提到了是基于操作系統互斥(mutex)實現的,操作系統實現線程之間切換需要從用戶態到內核態切換,成本非常高。

注:鎖只可以升級不可以降級,但是偏向鎖可以被重置為無鎖狀態。

最后,附上一張關于synchronized鎖升級流程圖(很全面很牛):

作者收藏很久的,synchronized鎖升級流程圖(很全面很牛)

注:由于文章中上傳的圖片會被壓縮,清晰度受到影響,可以關注并私信作者"鎖升級"獲取synchronized鎖升級流程圖(高清版)。

END

筆者是一位熱愛互聯網、熱愛互聯網技術、熱于分享的年輕人,如果您跟我一樣,我愿意成為您的朋友,分享每一個有價值的知識給您。喜歡作者的同學,點贊+轉發+關注哦!

點贊+轉發+關注,私信作者“讀書筆記”即可獲得BAT大廠面試資料、高級架構師VIP視頻課程等高質量技術資料。

BAT等一線互聯網面試資料和VIP高級架構師視頻

總結

以上是生活随笔為你收集整理的在c#中用mutex类实现线程的互斥_面试官经常问的synchronized实现原理和锁升级过程,你真的了解吗...的全部內容,希望文章能夠幫你解決所遇到的問題。

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