Java之synchronized的JVM底層實現原理精簡理解

1?synchronized的JVM底層原理實現的精簡理解

Java 虛擬機中的synchronized基于進入和退出Monitor對象(也稱為管程或監視器鎖)實現, 無論是顯式同步(synchronized作用在同步代碼塊,有明確的 monitorenter monitorexit 指令) 還是隱式同步synchronized作用在方法區,調用指令ACC_SYNCHRONIZED 標志)都是如此,都是使得Monitor對象里面的count計數期增加或者減少來實現,然后synchronized屬于重量級鎖,效率低下,因為監視器鎖(monitor)是依賴于底層的操作系統的Mutex Lock來實現的,而操作系統實現線程之間的切換時需要從用戶態轉換到核心態,這個狀態之間的轉換需要相對比較長的時間,時間成本相對較高,這也是為什么早期的synchronized效率低的原因,ReentrantLock底層實現依賴于特殊的CPU指令,比如發送lock指令和unlock指令,不需要用戶態和內核態的切換,所以效率高(這里和volatile底層原理類似)。

?

?

?

?

?

?

?

?

?

?

2 Java對象頭與Monitor的理解

1)Java對象的構成

在JVM中,對象在內存中的布局分為三塊區域:對象頭實例數據對齊填充

  • 實例變量:類的屬性數據信息,包括父類的屬性信息。
  • 填充數據:虛擬機要求對象起始地址必須是8字節的整數倍,這里和C語言的結構體內存對齊還是有點不一樣。
  • 對象頭:jvm中采用2個字節來存儲對象頭(如果對象是數組則會分配3個字,多出來的1個字記錄的是數組長度),其主要結構是由Mark Word Class Metadata Address?

虛擬機位數? ? ? ? ? 頭對象結構? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 作用
32/64bit? ? ? ? ? ? ? ?Mark Word? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?存儲對象的hashCode鎖信息分代年齡GC標志等信息
32/64bit? ? ? ? ? ? ? ?Class Metadata Address? ? ? ? ?類型指針指向對象的類元數據,JVM通過這個指針確定該對象是哪個類的實例。

?

32位JVM的Mark Word默認存儲結構如下

鎖狀態25bit4bit1bit是否是偏向鎖2bit 鎖標志位
無鎖狀態對象HashCode對象分代年齡001

Mark Word默認存儲結構外,還有如下可能變化的結構

?

?

?

?

?

?

2)monitor對象

輕量級鎖和偏向鎖是Java 6 對 synchronized 鎖進行優化后新增加,重量級鎖也就是通常說synchronized的對象鎖,鎖標識位為10,其中指針指向的是monitor對象,每個對象都存在著一個 monitor 與之關聯,對象與其 monitor 之間的關系有存在多種實現方式,如monitor可以與對象一起創建銷毀或當線程試圖獲取對象鎖時自動生成,但當一個 monitor 被某個線程持有后,它便處于鎖定狀態。在Java虛擬機(HotSpot)中,monitor是由ObjectMonitor實現的,其主要數據結構如下(位于HotSpot虛擬機源碼ObjectMonitor.hpp文件,C++實現的)
?

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,同時該線程進入 WaitSe t集合中等待被喚醒。若當前線程執行完畢也將釋放monitor(鎖)并復位變量的值,以便其他線程進入獲取monitor(鎖)

monitor對象存在于每個Java對象的對象頭中(存儲的指針的指向),synchronized鎖便是通過這種方式獲取鎖的,也是為什么Java中任意對象可以作為鎖的原因,同時也是notify/notifyAll/wait等方法存在于頂級對象Object中的原因

?

?

?

?

?

?

?

?

?

?

3?synchronized作用于代碼塊

?synchronized作用代碼塊后反編譯的字節碼關鍵如下

3: monitorenter  //進入同步方法
//..........省略其他  
15: monitorexit   //退出同步方法
16: goto          24
//省略其他.......
21: monitorexit //退出同步方法

從字節碼中可知同步語句塊的實現使用的是monitorenter 和 monitorexit 指令,其中monitorenter指令指向同步代碼塊的開始位置,monitorexit指令則指明同步代碼塊的結束位置,當執行monitorenter指令時,當前線程將試圖獲取 objectref(即對象鎖) 所對應的 monitor 的持有權,當 objectref 的 monitor 的進入計數器為 0,那線程可以成功取得 monitor,并將計數器值設置為 1,取鎖成功。如果當前線程已經擁有 objectref 的 monitor 的持有權,那它可以重入這個 monitor (關于重入性稍后會分析),重入時計數器的值也會加 1。倘若其他線程已經擁有 objectref 的 monitor 的所有權,那當前線程將被阻塞,直到正在執行線程執行完畢,即monitorexit指令被執行,執行線程將釋放 monitor(鎖)并設置計數器值為0 ,其他線程將有機會持有 monitor 。值得注意的是編譯器將會確保無論方法通過何種方式完成,方法中調用過的每條 monitorenter 指令都有執行其對應 monitorexit 指令,而無論這個方法是正常結束還是異常結束。為了保證在方法異常完成時 monitorenter 和 monitorexit 指令依然可以正確配對執行,編譯器會自動產生一個異常處理器,這個異常處理器聲明可處理所有的異常,它的目的就是用來執行 monitorexit 指令。從字節碼中也可以看出多了一個monitorexit指令,它就是異常結束時被執行的釋放monitor 的指令
?

?

?

?

?

?

?

?

?

?

?

4?synchronized作用于方法

synchronized作用代碼塊后反編譯的字節碼關鍵如下

 descriptor: ()V//方法標識ACC_PUBLIC代表public修飾,ACC_SYNCHRONIZED指明該方法為同步方法flags: ACC_PUBLIC, ACC_SYNCHRONIZEDCode:

?JVM可以從方法常量池中的方法表結構(method_info Structure) 中的 ACC_SYNCHRONIZED 訪問標志區分一個方法是否同步方法。當方法調用時,調用指令將會 檢查方法的 ACC_SYNCHRONIZED 訪問標志是否被設置,如果設置了,執行線程將先持有monitor(虛擬機規范中用的是管程一詞), 然后再執行方法,最后再方法完成(無論是正常完成還是非正常完成)時釋放monitor。在方法執行期間,執行線程持有了monitor,其他任何線程都無法再獲得同一個monitor。如果一個同步方法執行期間拋 出了異常,并且在方法內部無法處理此異常,那這個同步方法所持有的monitor將在異常拋到同步方法之外時自動釋放
?

?

?

?

?

?

?

?

?

?

?

?

5?synchronized的優化

鎖的狀態總共有四種,無鎖狀態偏向鎖輕量級鎖重量級鎖

其膨脹方向:無鎖——>偏向鎖——>輕量級鎖——>重量級鎖,并且膨脹方向不可逆。?

1)? 偏向鎖:

核心思想是,如果一個線程獲得了鎖,那么鎖就進入偏向模式,此時Mark Word 的結構也變為偏向鎖結構,當這個線程再次請求鎖時,無需再做任何同步操作,即獲取鎖的過程這樣就省去了大量有關鎖申請的操作,從而也就提供程序的性能,對于沒有鎖競爭的場合,偏向鎖有很好的優化效果,畢竟極有可能連續多次是同一個線程申請相同的鎖。但是對于鎖競爭比較激烈的場合,偏向鎖就失效了,因為這樣場合極有可能每次申請鎖的線程都是不相同的,因此這種場合下不應該使用偏向鎖,否則會得不償失,需要注意的是,偏向鎖失敗后,并不會立即膨脹為重量級鎖,而是先升級為輕量級鎖。下面我們接著了解輕量級鎖
?

2)??輕量級鎖:

對絕大部分的鎖,在整個同步周期內都不存在競爭”,注意這是經驗數據。需要了解的是,輕量級鎖所適應的場景是線程交替執行同步塊的場合如果存在同一時間訪問同一鎖的場合,就會導致輕量級鎖膨脹為重量級鎖

?

?

?

3) 、重量級鎖

重量級鎖是由輕量級鎖升級而來,同一時間有多個線程競爭鎖時,鎖就會被升級成重量級鎖,此時其申請鎖帶來的開銷也就變大

?

?

?

4)?、鎖消除

消除鎖是虛擬機另外一種鎖的優化,這種優化更徹底,在JIT編譯時,對運行上下文進行掃描,去除不可能存在競爭的鎖

部分參考博客:https://blog.csdn.net/javazejian/article/details/72828483

?

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

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

相關文章

三分鐘掌握Actor和CSP模型

點擊上方藍字進行關注前文傳送門:《三分鐘掌握共享內存模型和 Actor模型》, 一直想比較Actor模型與golang的CSP模型,經過一段時間的實戰記錄了本文。Actor vs CSP模型? 傳統多線程的的共享內存(ShareMemory)模型使用l…

DateTimeToUnix/UnixToDateTime 對接時間轉換

問題&#xff0c;通過毫秒數來解析出時間&#xff1a;&#xff08;很多對接的時候經常需要用到&#xff09; <?php $MyJson {"jingdong_vas_subscribe_get_responce":{"code":"0","item_code":"FW_GOODS-2236-1","…

【學生選課系統經典】VB與SQLSERVER連接:Windows應用工程案例

實驗任務描述 1 用VB6訪問SQLSERVER數據庫(兩種安全模式); 2 用VB6完成數據庫指定表上的數據顯示; 3 用VB6完成數據庫指定表上的數據插入、刪除和更新; 4 用VB6完成SQLSERVER2008數據庫用戶驗證。 一、數據庫系統 該實驗中,所要求的數據庫名稱為SCHOOL,總共涉及以下表:

丟失api-ms-win-crt-runtime-l1-1-0.dll

運行Cmder的時候提示&#xff1a;丟失api-ms-win-crt-runtime-l1-1-0.dll在網上找了一些方法&#xff0c;基本解決方法都是裝VC2015的運行時&#xff0c;但是我安裝的時候出錯&#xff0c;大家可以先試試。接著我就去解決安裝出錯這問題沒&#xff0c;折騰了半天也沒成功。后來…

《假如編程是魔法之零基礎看得懂的Python入門教程 》——(二)魔法實習生第一步了解魔杖的使用

學習目標 了解什么是開發環境了解python語言的環境安裝了解python語言編程的編輯器工具 目錄 第一篇&#xff1a;《假如編程是魔法之零基礎看得懂的Python入門教程 》——&#xff08;一&#xff09;既然你選擇了這系列教程那么我就要讓你聽得懂 第三篇&#xff1a;《假如編…

Java之synchronized可重入性的理解

1 synchronized可重入性的理解 當一個線程試圖操作一個由其他線程持有的對象鎖的臨界資源時&#xff0c;將會處于阻塞狀態&#xff0c;但當一個線程再次請求自己持有對象鎖的臨界資源時&#xff0c;如果當前鎖是重入性&#xff0c;會請求將會成功&#xff0c;如果當前鎖不是可…

onmouseover-onmouseout

<input type"checkbox" value"autoLogin" οnmοuseοver"block()" οnmοuseοut"none()">兩周內自動登錄 <div id"div1">為了您的信息安全請不要在網吧或公共電腦勾選此項</div> <script> functi…

mysql5.7 only_full_group_by_Mysql5.7及以上版本 ONLY_FULL_GROUP_BY報錯的解決方法

近期在開發過程中&#xff0c;因為項目開發環境連接的mysql數據庫是阿里云的數據庫&#xff0c;而阿里云的數據庫版本是5.6的。而測試環境的mysql是自己安裝的5.7。因此在開發過程中有小伙伴不注意寫了有關group by的sql語句。在開發環境中運行是正常的&#xff0c;而到了測試環…

一款高速的NET版的離線免費OCR

PaddleOCR.Onnx一款基于Paddle的OCR&#xff0c;項目使用ONNX模型&#xff0c;速度更快。本項目同時支持X64和X86的CPU上使用。本項目是一個基于PaddleOCR的C代碼修改并封裝的.NET的工具類庫。包含文本識別、文本檢測、基于文本檢測結果的統計分析的表格識別功能&#xff0c;同…

spring 注解簡單使用

一、通用注解 1、項目結構&#xff1a; 2、新建Person類&#xff0c;注解Component未指明id&#xff0c;則后期使用spring獲取實例對象時使用默認id"person"方式獲取或使用類方式獲取 package hjp.spring.annotation.commen;import org.springframework.stereotype.C…

selenium+python筆記3

#!/usr/bin/env python # -*- coding: utf-8 -*- """ desc:學習unittest的用法 注意setUp/setUpClass&#xff0c;tearDown/tearDownClass的區別 ① setUp():每個測試函數運行前運行 ② tearDown():每個測試函數運行完后執行 ③ setUpClass():必須使用classmeth…

【學生選課系統經典】C#與SQLSERVER連接:ASP.NET網站(服務器端,IIS發布)

實驗任務描述 1 用C#訪問SQLSERVER數據庫(兩種安全模式); 2 用C#完成數據庫指定表上的數據顯示; 3 用C#完成數據庫指定表上的數據插入、刪除和更新; 4 用C#完成數據庫用戶驗證。 此處使用ASP.NET工程來完成這個項目,和Windows應用不同的是:這個項目是在服務器上、依靠IIS服…

TCP包頭、UDP包頭、IP包頭、和MAC幀包頭詳細字段和包頭大小

1 TCP頭 TCP是一種可靠的、面向連接的字節流服務,頭部定義如下。 /*TCP頭定義,共20個字節*/ typedef struct _TCP_HEADER {short m_sSourPort;       // 源端口號16bitshort m_sDestPort;       // 目的端口號16bitunsigned int m_uiSequNum; …

經典面試題:用戶反映你開發的網站訪問很慢可能會是什么原因

原文鏈接&#xff1a;http://blog.csdn.net/lv_victor/article/details/53148421 問題場景&#xff1a;某個用戶向你反映說你開發的網站訪問速度很慢&#xff0c;但是該用戶訪問其他問題很正常&#xff0c;分析下原因、有哪些工具分析原因、怎么解決問題&#xff1f; 最近面試兩…

《假如編程是魔法之零基礎看得懂的Python入門教程 》——(三)使用初始魔法跟編程魔法世界打個招呼吧

學習目標 完成顯示魔法的使用——輸出print完成傳入魔法的使用——輸入input使魔法生效——運行python文件 目錄 第一篇&#xff1a;《假如編程是魔法之零基礎看得懂的Python入門教程 》——&#xff08;一&#xff09;既然你選擇了這系列教程那么我就要讓你聽得懂 第二篇&am…

查缺補漏系統學習 EF Core 6 (一)

推薦關注「碼俠江湖」加星標&#xff0c;時刻不忘江湖事掌握 ORM 開發方式是每一個 .NET 開發者所必備的技能&#xff0c;而且 .NET 平臺有很多優秀的 ORM 框架。很多人都會詬病 .NET 官方標配的 Entity Framework&#xff0c;感覺其笨重難用、性能低下。但其實經過多年發展&am…

mysql 5.5 mysqldump_mysql 5.5 mysqldump 原文翻譯

根據mysql 5.5第6.4章節理解和自己翻譯水平有限如有紕漏請指教,原文如下.6.4 使用mysqldump備份(Using mysqldump for Backups)首先多余的不用說了備份用來干什么大家都清楚。mysqldump備份分兩種輸出形式&#xff1a;1. 無--tab選項&#xff0c;輸出標準的SQL格式。輸出包含CR…

【經典回放】JavaScript學習詳細干貨筆記之(一)

【經典回放】JavaScript學習詳細干貨筆記之&#xff08;一&#xff09; 【經典回放】JavaScript學習詳細干貨筆記之&#xff08;二&#xff09; 【經典回放】JavaScript學習詳細干貨筆記之&#xff08;三&#xff09; 目錄 一、為什么要學JavaScript 二、JavaScript經典案例 …

Java Attach API

catalog 1. instrucment與Attach API 2. BTrace: VM Attach的兩種方式 3. Sun JVM Attach API 1. instrucment與Attach API JDK5中增加了一個包java.lang.instrucment&#xff0c;能夠對JVM底層組件進行訪問。在JDK 5中&#xff0c;Instrument 要求在運行前利用命令行參數或者系…

TCP之三次握手和四次揮手過程

1 TCP包頭里面的標志位 下圖為TCP頭部里面部分信息,入下標志位,每個標志位占一位。 標志位這里會涉及3個,ACK SYN FIN ACK:確認序號有效。 SYN:發起一個新連接。 FIN:釋放一個連接。 2 三次握手過程 第一次握手 Client將標志位SYN置1,隨機產生一個值seq=J,并將數…