sychronized原理(嚼碎了喂版)

先說一下心得吧,我們知道硬軟不分家,在學習底層原理的時候我們不需要死扣到底,沒必要把硬件方面全吃透,點到為止,學到能夠幫助理解代碼即可,我們的目標是寫出高性能的代碼,而不是創造出硬軟一體化高性能套件。不要一學底層就一股子牛勁死磕,至少我們現在不應該這樣,莫要本末倒置。(好吧其實是我在學的時候有點轉牛角尖了,一直問ai問題,仿佛是想把整個計算機領域吃透一般)希望這篇文章對大家有幫助,認真看完哦!歡迎指出理解有誤的地方!!!

先看看 Java 中 new 一個對象會有哪些信息被創建出來

在 HotSpot 虛擬機中,一個對象在堆內存的存儲布局可以劃分為三部分(以 64 位操作系統為例,不用在意 32 位下的情況,他會被淘汰…) :

對象頭在這里插入圖片描述

實例數據:存類中聲明的成員信息,如 int a = 2(占 4 字節)

對其填充:對象存儲必須按 8 字節對齊(64 位系統的默認配置),如果 對象頭+實例數據 所占的 bit 不是 8 的倍數,如為 65bit(隨便舉例),那么只這個部分會填充 7bit,變為 72bit(9 字節)。目的是:提高內存訪問效率:CPU 讀取內存時,對齊的數據能減少總線周期(如 64 位 CPU 一次讀 8 字節),避免偽共享(False Sharing):對齊后,不同對象不會共享同一緩存行。(硬件知識)

再來看看 synchronized 到底是什么模樣

人人都說 sy 重,重在哪里呢?

重量級鎖的實現是基于 monitor 機制的,那就先談談 Monitor(管程)

他是操作系統層面的一個東西,提供了一種結構化的方式來管理共享數據和并發訪問,其核心組成為 互斥鎖條件變量,是由操作系統的一些指令來控制的(這里不過多展開,因為我不會)

再說說 JVM 層面的具體實現:

JVM 中的 Monitor 是通過 C++實現的 ObjectMonitor 對象,當升級為重量級鎖時底層會創建這個對象,并把它的地址放在對應 Java 對象的 mark word 中(再去看看上面的圖),根據這個地址進行之后的一系列操作,字段有:

  • _owner: 指向當前持有鎖的線程。

  • _count: 記錄鎖的重入次數。

  • _EntryList: 等待獲取鎖的線程隊列(阻塞隊列)。

  • _WaitSet: 調用 wait() 方法后進入等待狀態的線程隊列。

再來說說為什么 sy 重:

在獲取、釋放等鎖相關的操作時,本質上都是操作這個 ObjectMonitor 對象,線程的阻塞、喚醒、調度都是操作系統的職責,必須通過操作系統內核來完成(調用內核中的底層方法)這就牽扯到了從用戶態內核態的切換

用戶態:

  • 用戶態的程序沒有權限直接操作其他線程的狀態(如從運行狀態切換到阻塞狀態)。

  • 用戶態程序也無法直接訪問和修改操作系統的線程調度隊列。

  • 這些操作涉及到對底層系統資源的訪問和管理,是操作系統的核心功能。

而這個切換是非常銷毀后資源的,會執行很多指令來完成這項操作

可以粗略的認為:用戶態是 CPU 執行應用程序代碼(如 Java 字節碼、Python 解釋器代碼),而內核態是 CPU 執行操作系統內核代碼(如文件讀寫,內存分配等底層操作),但要注意的是 用戶態和內核態的切換本質是 CPU 特權級別的變化,而不是僅僅是“誰在運行代碼”,所以說可以“粗略認為”,幫助理解即可

  • 系統調用是用戶態程序進入內核態的唯一方式。

  • 進入內核態需要保存當前用戶線程的上下文(寄存器狀態、程序計數器等,記錄執行到了哪里方便下次回來接著執行),然后切換到內核的代碼執行。

  • 從內核態返回用戶態時,需要恢復用戶線程的上下文。

  • 這個保存和恢復上下文的過程就是 上下文切換 (Context Switch),它是有開銷的,通常比用戶態的指令執行慢幾個數量級。(注意這里討論的是用戶態和內核態的上下文切換,有人可能會想到線程的上下文切換,他們不是同一個概念,但相同點是都會造成額外的開銷)

再來說說重量級鎖的操作流程:

當一個線程嘗試獲取一個對象的 Monitor 時:

  1. 如果 _owner 為空,線程成功獲取鎖,設置 _owner 為自身,_count 為 1。

  2. 如果 _owner 是當前線程,_count 加 1(重入)。

  3. 如果 _owner 是其他線程,當前線程進入 _EntryList 阻塞等待。

當一個線程釋放 Monitor 時:

  1. _count 減 1。

  2. 如果 _count 變為 0,釋放鎖,_owner 置空。

  3. 然后從 _EntryList_WaitSet 中喚醒一個或多個線程,讓它們有機會競爭鎖。

這里插播一下線程的幾個重要的狀態(操作系統層面)

BLOCKED、WAITTING(TIME)都不會消耗 CPU 資源,在進入這個狀態時會自動釋放占用的資源

只有 RUNNING 才會消耗資源

而 RUNNABLE 知識代表這個線程可以開始干活了,但是還沒有活干,等待 CPU 時間片分給他活,是不消耗資源的

而在 Java 層面

沒有 RUNNING 狀態

RUNNABLE 狀態就包含了 RUNNABLE 與 RUNNING

所以說,sy 重的核心原因是:線程的阻塞、喚醒和調度是操作系統的職責,必須通過內核來完成。

既然我們知道了導致 sy 重的原因是線程阻塞引起的,解決的方法當然就是不讓他阻塞咯,那么怎么讓他不阻塞捏?

無鎖化編程 CAS 應運而生,挑起了重擔,他通過讓線程自旋嘗試獲取鎖的方法來規避去阻塞等待的方式。

有人可能會問:CAS 自旋不是會造成 CPU 空轉嗎?這不也在浪費資源嗎?

是的,CAS 自旋消耗 CPU 資源,用戶態與內核態之間的切換亦會浪費資源。但仍選擇優化為 CAS 自旋的核心目的是 在“短時間鎖競爭”和“長時間鎖競爭”之間找到性能平衡(你想,如果在一個線程第一次嘗試獲取鎖失敗之后鎖立馬被釋放了,然而他卻去了阻塞隊列…這得多造孽呀,如果再堅持一下的話…或許我和她的結果就會不一樣了…😭,這樣看適當自旋一下還是非常好的)

sy 采用的是先自旋,再阻塞的策略。(她一直不搭理我,我也不能一直舔吧…我也是有尊嚴的!!!)

“先自旋后阻塞”是一種 折中策略,通過 動態適應鎖競爭情況,在 低延遲高吞吐 之間取得平衡。

  • 自旋:為短期鎖競爭優化響應速度。

  • 阻塞:為長期鎖競爭優化系統資源利用率。

一句話,先自旋,不行再阻塞(翻譯:先舔舔,不行咱就走唄,等著找下家~**)**

了解了整體思路,最后來看看 sy 中 偏向鎖、輕量級鎖以及重量級鎖的具體實現:

按照我們上面的分析,產物應該就是輕量級鎖(CAS)咯,我猜官方的想法是既然要走不阻塞這條路那就干脆極端一點來個無鎖判斷(偏向)得了,所以就又加了偏向鎖,干脆 CAS 操作都不做了,很徹底!

偏向鎖:

單線程競爭,當線程 A 第一次競爭到鎖時,通過修改 MarkWord 中的偏向線程 ID、偏向模式。如果不存在其他線程競爭,那么持有偏向鎖的線程將永遠不需要進行同步(JVM 不會執行任何額外的同步操作(如 CAS、系統調用、內核態切換等),而是直接允許線程訪問臨界區。) .

什么時候升級為輕量級鎖呢?

  • 調用了對象的 hashCode,但偏向鎖的對象 MarkWord 中存儲的是線程 id,如果調用 hashCode 會導致偏向鎖被撤銷,(輕量級鎖會在鎖記錄中記錄 hashCode,重量級鎖會在 Monitor 中記錄 hashCode)

  • 當有另外一個線程逐步來競爭鎖的時候,就不能再使用偏向鎖了,要升級為輕量級鎖,使用的是等到競爭出現才釋放鎖的機制

  • 競爭線程嘗試 CAS 更新對象頭失敗,會等到全局安全點(此時偏向鎖對應的 ThreadID 線程不會執行任何代碼)撤銷偏向鎖,同時檢查持有偏向鎖的線程是否還在執行:

    • 第一個線程正在執行 Synchronized 方法(處于同步塊),它還沒有執行完,其他線程來搶奪,該偏向鎖會被取消掉并出現鎖升級,此時輕量級鎖由原來持有偏向鎖的線程持有,繼續執行同步代碼塊,而正在競爭的線程會自動進入自旋等待獲得該輕量級鎖

    • 第一個線程執行完 Synchronized(退出同步塊),則將對象頭設置為無鎖狀態并撤銷偏向鎖,重新偏向。

題外話:Java15 以后逐步廢棄偏向鎖,需要手動開啟------->維護成本高

輕量級鎖

JVM 會為每個線程在當前線程的棧幀中創建用于存儲鎖記錄(Lock Record)的空間,官方稱為 DisplacedMarkWord。若一個線程獲得鎖時發現是輕量級鎖,會把鎖的 MarkWord 復制到自己的 DisplacedMarkWord 里面。然后線程嘗試用 CAS 將鎖的 MarkWord 替換為指向鎖記錄的指針。如果成功,當前線程獲得鎖,如果失敗,表示 MarkWord 已經被替換成了其他線程的鎖記錄,說明在與其它線程競爭鎖,當前線程就嘗試使用自旋來獲取鎖。

如果是自己執行了 synchronized 鎖重入,那么再添加一條 Lock Record 作為重入的計數

自旋 CAS:不斷嘗試去獲取鎖,能不升級就不往上捅,盡量不要阻塞(升級為重量級鎖)

輕量級鎖的釋放

在釋放鎖時,當前線程會使用 CAS 操作將 Displaced MarkWord 的內容復制回鎖的 MarkWord 里面。如果沒有發生競爭,那么這個復制的操作會成功。如果有其他線程因為自旋多次導致輕量級鎖升級成了重量級鎖,那么 CAS 操作會失敗,此時會進入重量級鎖解鎖流程

自旋一定程度和次數(Java8 之后是自適應自旋鎖------意味著自旋的次數不是固定不變的):

  • 線程如果自旋成功了,那下次自旋的最大次數會增加,因為 JVM 認為既然上次成功了,那么這一次也大概率會成功

  • 如果很少會自選成功,那么下次會減少自旋的次數甚至不自旋,避免 CPU 空轉

輕量鎖和偏向鎖的區別:

  • 爭奪輕量鎖失敗時,自旋嘗試搶占鎖

  • 輕量級鎖每次退出同步塊都需要釋放鎖,而偏向鎖是在競爭發生時才釋放鎖

重量級鎖

當線程嘗試獲取輕量級鎖失敗后,進入鎖膨脹,創建 ObjectMonitor 對象并將鎖中的 mark word 字段移入到該 Monitor 對象中,將該對象地址放入 mark word 中,接下來的流程在上文已經講過了

鎖釋放時通過 Monitor 地址找到對象,將 owner 設置為 null,喚醒 EntyList 中 BLOCKED 線程去搶鎖

補充一下 wait/notify 原理:

·Owner 線程發現條件不滿足,調用 wait 方法,即可進入 WaitSet 變為 WAITING 狀態

·BLOCKED 和 WAITING 的線程都處于阻塞狀態,不占用 CPU 時間片

·BLOCKED 線程會在 Owner 線程釋放鎖時喚醒

·WAITING 線程會在 Owner 線程調用 notify 或 notifyAll 時喚醒,但喚醒后并不意味著立刻獲得鎖,仍需進入

EntryList 重新競爭

完結撒花🎉

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

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

相關文章

Ngrok 配置:實現 Uniapp 前后端項目內網穿透

文章目錄 一、下載并安裝 ngrok二、配置 ngrok Authtoken三、啟動本地 uniapp 項目四、使用 ngrok 暴露本地服務五、通過公網 URL 訪問項目六、后端API項目的穿透問題排查 (uni-app 后端 API 示例)交互流程圖示 七、ngrok Web 界面 (本地監控)八、停止 ngrok總結 ngrok 是一款…

k8s灰度發布

基于 Traefik 的加權灰度發布-騰訊云開發者社區-騰訊云 Traefik | Traefik | v1.7 Releases traefik/traefik GitHub 從上面連接下載后上傳到harbor虛擬機 vagrant upload /C/Users/HP280/Downloads/traefik 下載配置文件 wget -c http://raw.githubusercontent.com/conta…

win10-django項目與mysql的基本增刪改查

以下都是在win10系統下,django項目的orm框架對本地mysql的表的操作 models.py----->即表對應的類所在的位置 在表里新增數據 1.引入表對應的在models.py中的類class 2.在views.py中使用函數:類名.objects.create(字段名值,字段名"值"。。。…

`ParameterizedType` 和 `TypeVariable` 的區別

在 Java 的泛型系統中,ParameterizedType 和 TypeVariable 是兩個不同的類型表示,它們都屬于 java.lang.reflect.Type 接口的子接口。兩者都在反射(Reflection)中用于描述泛型信息,但用途和含義不同。 🌟 一…

PR-2021

推薦深藍學院的《深度神經網絡加速:cuDNN 與 TensorRT》,課程面向就業,細致講解CUDA運算的理論支撐與實踐,學完可以系統化掌握CUDA基礎編程知識以及TensorRT實戰,并且能夠利用GPU開發高性能、高并發的軟件系統&#xf…

unity使用ZXing.Net生成二維碼

下載鏈接 https://github.com/micjahn/ZXing.Net 放到Plugins下即可使用

Ubuntu 編譯SRS和ZLMediaKit用于視頻推拉流

SRS實現視頻的rtmp webrtc推流 ZLMediaKit編譯生成MediaServer實現rtsp推流 SRS指定某個固定網卡,修改程序后重新編譯 打開SRS-4.0.0/trunk/src/app/srs_app_rtc_server.cpp,在 232 行后面添加: ZLMediaKit編譯后文件存放在ZLMediakit/rele…

如何備考GRE?

1.引言 GRE和雅思不太相同,首先GRE是美國人的考試,思維方式和很多細節和英系雅思不一樣。所以底層邏輯上我覺得有點區別。 難度方面,我感覺GRE不容易考低分,但考高分較難。雅思就不一樣了不僅上限難突破,下限還容易6…

uniapp|商品列表加入購物車實現拋物線動畫效果、上下左右拋入、多端兼容(H5、APP、微信小程序)

以uniapp框架為基礎,詳細解析商品列表加入購物車拋物線動畫的實現方案。通過動態獲取商品點擊位置與購物車坐標,結合CSS過渡動畫模擬拋物線軌跡,實現從商品圖到購物車圖標的動態效果。 目錄 核心實現原理坐標動態計算拋物線軌跡模擬?動畫元素控制代碼實現詳解模板層設計腳本…

React中使用openLayer畫地圖

OpenLayers(簡稱ol)是一個?開源的WebGIS前端開發庫?,基于JavaScript實現,主要用于在網頁中嵌入動態二維地圖。 官方網站: https://openlayers.org 中文官網: https://openlayers.vip 大家可以去參考學習…

WHAT - 緩存命中 Cache Hit 和緩存未命中 Cache Miss

文章目錄 一、什么是緩存命中?二、前端開發要知道哪些緩存機制(以及命中條件)?1. 瀏覽器緩存(主要針對靜態資源)常見的緩存位置關鍵 HTTP 頭字段(決定命中與否) 2. 前端應用層緩存&a…

10 個可靠的 Android 文件傳輸應用程序

Android 文件傳輸是 Android 用戶的常見需求。我們經常需要將文件從一臺 Android 設備傳輸到 PC 或 Mac。但我們怎樣才能做到這一點呢?俗話說,工欲善其事,必先利其器。因此,首先了解 10 個鋒利的 Android 文件傳輸應用程序&#x…

AlphaEvolve:LLM驅動的算法進化革命與科學發現新范式

AlphaEvolve:LLM驅動的算法進化革命與科學發現新范式 本文聚焦Google DeepMind最新發布的AlphaEvolve,探討其如何通過LLM與進化算法的結合,在數學難題突破、計算基礎設施優化等領域實現革命性進展。從48次乘法優化44矩陣相乘到數據中心資源利…

Java大師成長計劃之第24天:Spring生態與微服務架構之分布式配置與API網關

📢 友情提示: 本文由銀河易創AI(https://ai.eaigx.com)平臺gpt-4-turbo模型輔助創作完成,旨在提供靈感參考與技術分享,文中關鍵數據、代碼與結論建議通過官方渠道驗證。 在微服務架構中,如何管理…

eSwitch manager 簡介

eSwitch manager 的定義和作用 eSwitch manager 通常指的是能夠配置和管理 eSwitch(嵌入式交換機)的實體或接口。在 NVIDIA/Mellanox 的網絡架構中,Physical Function(PF)在 switchdev 模式下充當 eSwitch manager&am…

最新開源 TEN VAD 與 Turn Detection 讓 Voice Agent 對話更擬人 | 社區來稿

關鍵詞:對話式 AI | 語音智能體 | Voice Agent | VAD | 輪次檢測 | 聲網 | TEN GPT-4o 所展示對話式 AI 的新高度,正一步步把我們在電影《Her》中看到的 AI 語音體驗變成現實。AI 的語音交互正在變得更豐富、更流暢、更易用,成為構建多模態智…

AI實踐用例---日程規劃(通用日程管理文件ICS)靈感踩坑日常

我是一位踐行獨立開發者之路的菜鳥開發者。 由于執行力較差,常常有很多想法但是很多時候沒有去踐行。 所以我有了讓大模型為我生成日程安排的想法,這確實可以,很簡單。只需要將你的想法告訴ai就行了。 例如: 發給AI的提示詞: 我想你幫我對,嗯,未來的一年做一個嗯,大…

大疆無人機??DRC 鏈路

在大疆上云API中,??DRC 鏈路??通常指 ??Device-Cloud Remote Control Link(設備-云端遠程控制鏈路)??,它是無人機(或設備)與云端服務之間建立的??實時控制與數據傳輸通道??,用于實現…

tomcat一閃而過,按任意鍵繼續以及控制臺中文亂碼問題

問題描述 今天在打開tomcat,啟動startup.bat程序時 tomcat直接閃退,后面查找資料后發現,可以通過編輯startup.bat文件內容,在最后一行加入pause即可讓程序不會因為異常而終止退出 這樣方便查看tomcat所爆出的錯誤: 然后,我明確看到我的tomcat啟動程序顯示如下的內容,沒有明確…

中大型水閘安全監測系統解決方案

一、方案概述 中大型水閘作為水利工程的重要組成部分,承擔著調節水位、控制水流、防洪排澇等多重功能,在防洪減災、水資源配置、生態環境改善等方面發揮著巨大作用。然而,由于歷史原因,許多水閘存在建設標準偏低、質量較差、配套設…