【多線程】synchronized底層實現的方式

前言

在java 開發中對于鎖的應用非常的常見,如果對于什么時候該用什么鎖,以及鎖實現的原理有所不知道的,或者面試過程中面試官問你不知道怎么回答的,歡迎來看下面的文章

1、synchronized和ReentrantLock的區別

在這里插入圖片描述

2、synchronized的一些特性和底層原理的實現

2.1 synchronized 的鎖升級過程

在 JVM 中,鎖升級是不可逆的,即一旦鎖被升級為下一個級別的鎖,就無法再降級。

首先默認的無鎖狀態,當我們加鎖以后,可能并沒有多個線程去競爭鎖,此時我們可以默認為只有一個線程要獲取鎖,即偏向鎖,當鎖轉為偏向鎖以后,被偏向的線程在獲取鎖的時候就不需要競爭,可以直接執行。

當確實存在少量線程競爭鎖的情況時,偏向鎖顯然不能再繼續使用了,但是如果直接調用重量級鎖在輕量鎖競爭的情況下并不劃算,因為競爭壓力不大,所以往往需要頻繁的阻塞和喚醒線程,這個過程需要調用操作系統的函數去切換 CPU 狀態從用戶態轉為核心態。因此,可以直接令等待的線程自旋,避免頻繁的阻塞喚醒 ,此時升級為輕量級鎖

當競爭加大時,線程往往要等待比較長的時間才能獲得鎖,此時在等待期間保持自旋會白白占用 CPU 時間,此時就需要升級為重量級鎖,即 Monitor 鎖,JVM 通過指令調用操作系統函數阻塞和喚醒線程。

2.2 synchronized的鎖優化

2.2.1 自適應自旋鎖

自旋鎖依賴于 CAS,我們可以手動的設置 JVM 的自旋鎖自旋次數,但是往往很難確定適當的自旋次數,如果自旋次數太少,那么可能會引起不必要的鎖升級,而自旋次數太長,又會影響性能。在 JDK6 中,引入了自適應自旋鎖的機制,對于同一把鎖,當線程通過自旋獲取鎖成功了,那么下一次自旋次數就會增加,而相反,如果自旋鎖獲取失敗了,那么下一次在獲取鎖的時候就會減少自旋次數。

2.2.2 鎖消除

在一些方法中,有些加鎖的代碼實際上是永遠不會出現鎖競爭的,比如 Vector 和 Hashtable 等類的方法都使用 synchronized 修飾,但是實際上在單線程程序中調用方法,JVM 會檢查是否存在可能的鎖競爭,如果不存在,會自動消除代碼中的加鎖操作。

2.2.3 鎖粗化

我們常說,鎖的粒度往往越細越好,但是一些不恰當的范圍可能反而引起更頻繁的加鎖解鎖操作,比如在迭代中加鎖,JVM 會檢測同一個對象是否在同一段代碼中被頻繁加鎖解鎖,從而主動擴大鎖范圍,避免這種情況的發生。

2.3 synchronized的底層原理的實現

synchronized 意為同步,它可以用于修飾靜態方法,實例方法,或者一段代碼塊。其中修飾代碼塊和方法有一點不同
它是一種可重入的對象鎖。當修飾靜態方法時,鎖對象為類;當修飾實例方法時,鎖對象為實例;當修飾代碼塊時,鎖可以是任何非 null 的對象。由于其底層的實現機制,synchronized 的鎖又稱為監視器鎖。

同步代碼塊
    public void synchronizedMethod3() {synchronized(this) {System.out.println("synchronizedMethod3");}}

反編譯之后:
在這里插入圖片描述

monitorenter      // 嘗試獲取對象鎖  
...  
monitorexit       // 正常退出同步塊(偏移量 15)  
goto 23  
monitorexit       // 異常退出同步塊(偏移量 21)  
athrow  

這里的 monitorenter 與 monitorexit 即是線程獲取 synchronized 鎖的過程。

當線程試圖獲取對象鎖的時候,根據 monitorenter 指令:

如果 Monitor 的進入數為0,則該線程進入monitor,然后將進入數設置為1,該線程即為 Monitor 的所有者;
如果線程已經占有該 monitor,只是重新進入,則進入 Monitor 的進入數加1(可重入); 如果其他線程已經占用了
monitor,則該線程進入阻塞狀態,直到 Monitor 的進入數為0,再重新嘗試獲取 Monitor 的所有權; 當線程執行完以后,根據
monitorexit 指令:

當執行 monitorexit 指令后,Monitor 的進入數 -1; 如果 – 1 后 Monitor 進入數為 0,則該線程不再擁有這個鎖,退出 monitor; 如果 – 1 后 Monitor 進入數仍不為0,則線程繼續持有這個鎖,重復上述過程直到使用完畢。

特點:

  • 顯式生成 monitorenter 和 monitorexit 指令 進行獲取鎖和釋放鎖
  • 異常表中注冊兩個退出路徑,確保鎖必然釋放
同步方法
public synchronized void syncMethod() {  System.out.println("同步方法");  
}  
關鍵字節碼:flags: ACC_PUBLIC, ACC_SYNCHRONIZED  

通過 ACC_SYNCHRONIZED 標志隱式實現鎖機制是 Java 中 synchronized 關鍵字的核心實現原理。 在 Java
虛擬機(JVM)中,synchronized 關鍵字修飾的方法或代碼塊會通過在方法的 access_flags 字段中添加
ACC_SYNCHRONIZED 標志來標識該方法為同步方法。這個標志告訴 JVM,該方法需要進行同步操作。

同步機制的隱式實現 當一個方法被標記為 ACC_SYNCHRONIZED 時,JVM 在方法調用時會隱式地執行加鎖和解鎖操作。具體來說:

加鎖:在方法執行前,線程需要獲取與該方法相關的監視器鎖(monitor)。如果鎖已被其他線程持有,則當前線程會被阻塞,直到鎖被釋放。
解鎖:當方法正常完成或拋出異常時,鎖會被自動釋放。這確保了方法的原子性和可見性。

特點:

  • 通過 ACC_SYNCHRONIZED 標志隱式實現鎖機制
  • JVM 在方法調用和返回時自動插入鎖操作
實現原理

synchronized 是對象鎖,在 JDK6 引入鎖升級機制后,synchronized 的鎖實際上分為了偏向鎖、輕量級鎖和重量級鎖三種,這三者都依賴于對象頭中 MarkWord 的數據的改變。

對象

每個 Java 對象在內存中分為 對象頭(Header) 、 實例數據(Instance Data) 和 對齊填充(Padding)
在這里插入圖片描述
對象頭是實現鎖的核心區域,其結構如下

![在這里插入圖片描述](https://i-blog.csdnimg.cn/direct/758316d58cc84158bcca7804c211126d.png

其中markWord的樣子如下:
在這里插入圖片描述

2.3 重量級鎖與監視器

synchronized 的對象鎖是基于監視器對象 Monitor 實現的,而根據上文,我們知道鎖信息存儲于對象自己的 MarkWord 中,那么 Monitor 和 對象又是什么關系呢?

實際上,在對象在創建之初就會在 MarkWord 中關聯一個 Monitor 對象 ,當鎖升級到重量級鎖時,markWord存儲指向 Monitor 對象的指針。

Monitor 對象在 JVM 中基于 ObjectMonitor 實現,代碼如下:

ObjectMonitor() {_header       = NULL;_count        = 0; // 持有鎖次數_waiters      = 0,_recursions   = 0;_object       = NULL;_owner        = NULL; // 當前持有鎖的線程_WaitSet      = NULL; // 等待隊列,處于wait狀態的線程,會被加入到_WaitSet_WaitSetLock  = 0 ;_Responsible  = NULL ;_succ         = NULL ;_cxq          = NULL ;FreeNext      = NULL ;_EntryList    = NULL ; // 阻塞隊列,處于等待鎖block狀態的線程,會被加入到該列表_SpinFreq     = 0 ;_SpinClock    = 0 ;OwnerIsThread = 0 ;
}

ObjectMonitor 中有兩個隊列,_WaitSet 和 _EntryList,用來保存 ObjectWaiter 對象列表( 每個等待鎖的線程都會被封裝成 ObjectWaiter 對象 ),_owner 指向持有 ObjectMonitor 對象的線程,當多個線程同時訪問一段同步代碼時:
首先會進入 _EntryList 集合,當線程獲取到對象的 Monitor 后,進入 _Owner 區域并把 monitor 中的 owner 變量設置為當前線程,同時 monitor中的計數器 count 加1;
若線程調用 wait() 方法,將釋放當前持有的 monitor,owner 變量恢復為 null,count 自減1,同時該線程進入 WaitSet集合中等待被喚醒;
若當前線程執行完畢,也將釋放 Monitor 并復位 count 的值,以便其他線程進入獲取 Monitor;
這也解釋了為什么 notify() 、notifyAll()和wait() 方法會要求在同步塊中使用,因為這三個方法都需要獲取 Monitor 對象,而要獲取 Monitor,就必須使用 monitorenter指令。

2.4 synchronized線程調用時的阻塞場景


public class Example {public synchronized void methodA() { ... }public synchronized void methodB() { ... }
}

/線程1調用obj.methodA(),線程2調用obj.methodB() → 線程2阻塞,直到methodA釋放鎖
所以再用的時候盡可能的縮小鎖的范圍,代碼塊加鎖

總結

每個對象在內存中分為三個部分示例數據(存放類信息)、對象頭和對齊填充,其中對象頭有個重要的組成部分markWord,markWord中存儲一些鎖相關的引用,通過markWord中的鎖標志位,我們可以清楚的知道鎖的級別,是重量級鎖,還是輕量級鎖,還是偏向鎖,當是重量級鎖的時候,markWord中存儲了對監視器Monitor 對象的引用,synchronized 的對象鎖是基于監視器對象 Monitor 實現的,在對象在創建之初就會在 MarkWord 中關聯一個 Monitor 對象 ,當鎖升級到重量級鎖時,markWord存儲指向 Monitor 對象的指針。而要獲取 Monitor,就必須使用 monitorenter指令,可重入的表現是,Monitor里面有一個count,一次monitorenter后count 加1,一次monitorExit 后count減1

參考文章:https://cloud.tencent.com/developer/article/2120268?pos=comment

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74391.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74391.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74391.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Pytorch中Tensorboard的學習

1、Tensorboard介紹 TensorBoard 是 TensorFlow 開發的一個可視化工具,用于幫助用戶理解和調試機器學習模型的訓練過程。盡管它最初是為 TensorFlow 設計的,但通過 PyTorch 的 torch.utils.tensorboard 模塊,PyTorch 用戶也可以方便地使用 Te…

ETL 自動化:提升數據處理效率與準確性的核心驅動力

在數字化轉型的浪潮中,數據已成為企業戰略資產,高效處理數據的能力直接關系到企業的競爭力。ETL(Extract, Transform, Load)自動化作為數據處理領域的關鍵技術,正逐漸成為企業在數據時代脫穎而出、實現高效運營與精準決…

std::endl為什么C++ 智能提示是函數?

在使用vscode 的C智能提示后&#xff0c;輸入endl 后&#xff0c;提示的卻是std::endl(basic_ostream<CharT, Traits> &os), 感覺比較奇怪&#xff0c;各種代碼里都是直接用的std::endl 啊&#xff0c; 這里怎么變成函數了呢&#xff1f; 在 C 中&#xff0c;std::en…

簡潔、實用、無插件和更安全為特點的WordPress主題

簡站WordPress主題是一款以簡潔、實用、無插件和更安全為特點的WordPress主題&#xff0c;自2013年創立以來&#xff0c;憑借其設計理念和功能優勢&#xff0c;深受用戶喜愛。以下是對簡站WordPress主題的詳細介紹&#xff1a; 1. 設計理念 簡站WordPress主題的核心理念是“崇…

數據結構篇:空間復雜度和時間復雜度

目錄 1.前言&#xff1a; 1.1 學習感悟 1.2 數據結構的學習之路(初階) 2.什么是數據結構和算法 2.1 數據結構和算法的關系 2.2 算法的重要性 2.3 如何衡量算法的好壞 3.時間復雜度 3.1 時間復雜度的概念 3.2 大O的漸進表示法 O() 4.空間復雜度 5. 常見的時間復雜度和…

node-ddk,electron,截屏封裝(js-web-screen-shot)

node-ddk 截屏封裝(js-web-screen-shot) https://blog.csdn.net/eli960/article/details/146207062 也可以下載demo直接演示 http://linuxmail.cn/go#node-ddk 感謝/第三方 本截屏工具, 使用的是: js-web-screen-shot https://www.npmjs.com/package/vue-web-screen-shot…

泰坦軍團攜手順網旗下電競連鎖品牌樹呆熊 共創電競新紀元

在電競行業的浪潮中&#xff0c;品牌之間的戰略合作愈發成為推動市場前行的重要動力。最近&#xff0c;電競顯示器領域領軍品牌泰坦軍團高層領導出席順網旗下電競連鎖品牌樹呆熊十周年盛典。會議現場&#xff0c;雙方高層領導宣布泰坦軍團與樹呆熊正式達成戰略合作伙伴關系。 在…

HandyJSON原理

HandyJSON 的優勢 JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式, 應用廣泛. 在 App 的使用過程中, 服務端給移動端發送的大部分都是 JSON 數據, 移動端需要解析數據才能做進一步的處理. 在解析JSON數據這一塊, 目前 Swift 中流行的框架基本上是 SwiftyJSON, …

信號的產生和保存

信號的產生 信號就是操作系統對用戶操作做出的反應&#xff0c;但它的本質就是往操作系統寫入信號&#xff0c;這是由操作系統的結構決定的。通過修改比特位來告訴操作系統接收信號和傳了幾號信號。 也正是因為我們身為用戶無法親自修改內核數據&#xff0c;所以我們需要通過操…

在C++ Qt中集成Halcon窗口并實現跨平臺兼容和大圖加載

目錄 1. Halcon窗口嵌入Qt Widget 2. 處理大圖加載 3. 多線程優化顯示 4. 跨平臺兼容性 1. Halcon窗口嵌入Qt Widget 將Halcon的HWindow控件嵌入到Qt的QWidget容器中,利用系統原生句柄實現跨平臺。 #include <HalconCpp.h> #include <QWidget>class HalconWi…

深度學習技術與應用的未來展望:從基礎理論到實際實現

深度學習作為人工智能領域的核心技術之一&#xff0c;近年來引起了極大的關注。它不僅在學術界帶來了革命性的進展&#xff0c;也在工業界展現出了廣泛的應用前景。從圖像識別到自然語言處理&#xff0c;再到強化學習和生成對抗網絡&#xff08;GAN&#xff09;&#xff0c;深度…

藍光三維掃描技術:汽車零部件檢測的精準高效之選

——汽車方向盤配件、保險杠塑料件、鈑金件檢測項目 汽車制造工業的蓬勃發展&#xff0c;離不開強大的零部件制造體系作支撐。汽車零部件作為汽車工業的基礎&#xff0c;其設計水平、制造工藝、質量控制手段逐漸與國際標準接軌&#xff0c;對于零部件面差、孔位、圓角、特征線…

數據庫聯表Sql語句建一個新表(MySQL,Postgresql,SQL server)

數據庫聯表Sql語句建一個新表(MySQL,Postgresql,SQL server) 如果你想基于 SELECT USERS.ID,USERS.NAME,USERS.EMAIL,USERS.ID_CARD,USERS.V_CARD,USERS.ADDRESS,v_card.type,v_card.amount FROM USERS JOIN v_card on USERS.V_CARDv_card.v_card 這個查詢結果創建一個新表&am…

六十天前端強化訓練之第三十天之深入解析Vue3電商項目:TechStore全棧實踐(文結尾附有源代碼)

歡迎來到編程星辰海的博客講解 看完可以給一個免費的三連嗎&#xff0c;謝謝大佬&#xff01; 目錄 深入解析Vue3電商項目&#xff1a;TechStore全棧實踐 一、項目架構設計 二、核心功能實現 三、組合式API深度實踐 四、性能優化實踐 五、項目擴展方向 六、開發經驗總結…

【人工智能】機器學習中的評價指標

機器學習中的評價指標 在機器學習中&#xff0c;評估指標&#xff08;Evaluation Metrics&#xff09;是衡量模型性能的工具。選擇合適的評估指標能夠幫助我們更好地理解模型的效果以及它在實際應用中的表現。 一般來說&#xff0c;評估指標主要分為三大類&#xff1a;分類、…

不同機床對螺桿支撐座的要求有哪些不同?

螺桿支撐座是機械設備中重要的支撐部件&#xff0c;其選擇直接影響到設備的穩定性和使用壽命&#xff0c;尤其是在機床中&#xff0c;不同的機床對螺桿支撐座的要求也是不同的。 1、精度&#xff1a;精密測量用的基準平面和精密機床機械的檢驗測量設備&#xff0c;需要使用高精…

在Spring Boot中,可以通過實現一些特定的接口來拓展Starter

在Spring Boot中&#xff0c;開發者可以通過實現一些特定的接口來拓展Starter。這些接口允許開發者自定義Spring Boot應用程序的配置和行為&#xff0c;從而創建功能豐富且易于使用的Starter。以下是一些關鍵的接口&#xff0c;用于拓展Starter&#xff1a; EnvironmentPostPro…

深入理解 tree 命令行工具:目錄結構可視化的利器

文章目錄 前言1. 什么是 tree 命令&#xff1f;安裝 tree 2. tree 的基本用法顯示當前目錄的樹狀結構顯示指定目錄的樹狀結構 3. tree 的常用選項3.1 顯示隱藏文件3.2 排除特定目錄或文件3.3 限制遞歸深度3.4 顯示文件大小3.5 顯示文件的權限信息3.6 將輸出保存到文件 4. 實際應…

Federated learning client selection algorithm based on gradient similarity閱讀

基于梯度相似性的聯邦學習客戶端選擇算法 Abstract 摘要introduction**背景****目的****結論****結果****討論****思路** 鏈接&#xff1a;https://link.springer.com/article/10.1007/s10586-024-04846-0 三區 Abstract 摘要 聯邦學習&#xff08;FL&#xff09;是一種創新的…

【測試工具】如何使用 burp pro 自定義一個攔截器插件

在 Burp Suite 中&#xff0c;你可以使用 Burp Extender 編寫自定義攔截器插件&#xff0c;以攔截并修改 HTTP 請求或響應。Burp Suite 支持 Java 和 Python (Jython) 作為擴展開發語言。以下是一個完整的流程&#xff0c;介紹如何創建一個 Burp 插件來攔截請求并進行自定義處理…