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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

synchronized 底层了解一下...

發(fā)布時間:2025/3/15 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 synchronized 底层了解一下... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

背景

在 JDK1.5 之前,面對 Java 并發(fā)問題, synchronized 是一招鮮的解決方案:

  • 普通同步方法,鎖上當(dāng)前實(shí)例對象

  • 靜態(tài)同步方法,鎖上當(dāng)前類 Class 對象

  • 同步塊,鎖上括號里面配置的對象

  • 拿同步塊來舉例:

    public?void?test(){synchronized?(object)?{i++;} }

    經(jīng)過?javap -v?編譯后的指令如下:


    monitorenter?指令是在編譯后插入到同步代碼塊的開始位置;monitorexit是插入到方法結(jié)束和異常的位置(實(shí)際隱藏了try-finally),每個對象都有一個 monitor 與之關(guān)聯(lián),當(dāng)一個線程執(zhí)行到 monitorenter 指令時,就會獲得對象所對應(yīng)的?monitor?的所有權(quán),也就獲得到了對象的鎖

    當(dāng)另外一個線程執(zhí)行到同步塊的時候,由于它沒有對應(yīng)?monitor?的所有權(quán),就會被阻塞,此時控制權(quán)只能交給操作系統(tǒng),也就會從?user mode?切換到?kernel mode, 由操作系統(tǒng)來負(fù)責(zé)線程間的調(diào)度和線程的狀態(tài)變更, 需要頻繁的在這兩個模式下切換(上下文轉(zhuǎn)換)。這種有點(diǎn)競爭就找內(nèi)核的行為很不好,會引起很大的開銷,所以大家都叫它重量級鎖,自然效率也很低,這也就給很多童鞋留下了一個根深蒂固的印象 ——?synchronized關(guān)鍵字相比于其他同步機(jī)制性能不好

    鎖的演變

    來到 JDK1.6,要怎樣優(yōu)化才能讓鎖變的輕量級一些?答案就是:

    輕量級鎖:CPU CAS

    如果 CPU 通過簡單的 CAS 能處理加鎖/釋放鎖,這樣就不會有上下文的切換,較重量級鎖而言自然就輕了很多。但是當(dāng)競爭很激烈,CAS 嘗試再多也是浪費(fèi) CPU,權(quán)衡一下,不如升級成重量級鎖,阻塞線程排隊(duì)競爭,也就有了輕量級鎖升級成重量級鎖的過程


    程序員在追求極致的道路上是永無止境的,HotSpot 的作者經(jīng)過研究發(fā)現(xiàn),大多數(shù)情況下,鎖不僅不存在多線程競爭,而且總是由同一個線程多次獲得,同一個線程反復(fù)獲取鎖,如果還按照輕量級鎖的方式獲取鎖(CAS),也是有一定代價的,如何讓這個代價更小一些呢?

    偏向鎖

    偏向鎖實(shí)際就是鎖對象潛意識「偏心」同一個線程來訪問,讓鎖對象記住線程 ID,當(dāng)線程再次獲取鎖時,亮出身份,如果同一個 ID 直接就獲取鎖就好了,是一種?load-and-test?的過程,相較 CAS 自然又輕量級了一些

    可是多線程環(huán)境,也不可能只是同一個線程一直獲取這個鎖,其他線程也是要干活的,如果出現(xiàn)多個線程競爭的情況,也就有了偏向鎖升級的過程


    這里可以先思考一下:偏向鎖可以繞過輕量級鎖,直接升級到重量級鎖嗎?

    都是同一個鎖對象,卻有多種鎖狀態(tài),其目的顯而易見:

    占用的資源越少,程序執(zhí)行的速度越快

    偏向鎖,輕量鎖,它倆都不會調(diào)用系統(tǒng)互斥量(Mutex Lock),只是為了提升性能,多出的兩種鎖的狀態(tài),這樣可以在不同場景下采取最合適的策略,所以可以總結(jié)性的說:

    • 偏向鎖:無競爭的情況下,只有一個線程進(jìn)入臨界區(qū),采用偏向鎖

    • 輕量級鎖:多個線程可以交替進(jìn)入臨界區(qū),采用輕量級鎖

    • 重量級鎖:多線程同時進(jìn)入臨界區(qū),交給操作系統(tǒng)互斥量來處理

    到這里,大家應(yīng)該理解了全局大框,但仍然會有很多疑問:

  • 鎖對象是在哪存儲線程 ID 才可以識別同一個線程的?

  • 整個升級過程是如何過渡的?

  • 想理解這些問題,需要先知道 Java 對象頭的結(jié)構(gòu)

    認(rèn)識 Java 對象頭

    按照常規(guī)理解,識別線程 ID 需要一組 mapping 映射關(guān)系來搞定,如果單獨(dú)維護(hù)這個 mapping 關(guān)系又要考慮線程安全的問題。奧卡姆剃刀原理,Java 萬物皆是對象,對象皆可用作鎖,與其單獨(dú)維護(hù)一個 mapping 關(guān)系,不如中心化將鎖的信息維護(hù)在 Java 對象本身上

    Java 對象頭最多由三部分構(gòu)成:

  • MarkWord

  • ClassMetadata Address

  • Array Length (如果對象是數(shù)組才會有這部分)

  • 其中?Markword?是保存鎖狀態(tài)的關(guān)鍵,對象鎖狀態(tài)可以從偏向鎖升級到輕量級鎖,再升級到重量級鎖,加上初始的無鎖狀態(tài),可以理解為有 4 種狀態(tài)。想在一個對象中表示這么多信息自然就要用位存儲,在 64 位操作系統(tǒng)中,是這樣存儲的(注意顏色標(biāo)記),想看具體注釋的可以看 hotspot(1.8) 源碼文件?path/hotspot/src/share/vm/oops/markOop.hpp?第 30 行


    有了這些基本信息,接下來我們就只需要弄清楚,MarkWord 中的鎖信息是怎么變化的

    認(rèn)識偏向鎖

    單純的看上圖,還是顯得十分抽象,作為程序員的我們最喜歡用代碼說話,貼心的 openjdk 官網(wǎng)提供了可以查看對象內(nèi)存布局的工具 JOL (java object layout)

    Maven Package

    <dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.14</version> </dependency>

    Gradle Package

    implementation?'org.openjdk.jol:jol-core:0.14'

    接下來我們就通過代碼來深入了解一下偏向鎖吧

    注意:

    上圖(從左到右) 代表?高位 -> 低位

    JOL 輸出結(jié)果(從左到右)代表?低位 -> 高位

    來看測試代碼

    場景1

    public?static?void?main(String[]?args)?{Object?o?=?new?Object();log.info("未進(jìn)入同步塊,MarkWord 為:");log.info(ClassLayout.parseInstance(o).toPrintable());synchronized?(o){log.info(("進(jìn)入同步塊,MarkWord 為:"));log.info(ClassLayout.parseInstance(o).toPrintable());}}

    來看輸出結(jié)果:


    上面我們用到的 JOL 版本為?0.14, 帶領(lǐng)大家快速了解一下位具體值,接下來我們就要用?0.16?版本查看輸出結(jié)果,因?yàn)檫@個版本給了我們更友好的說明,同樣的代碼,來看輸出結(jié)果:


    看到這個結(jié)果,你應(yīng)該是有疑問的,JDK 1.6 之后默認(rèn)是開啟偏向鎖的,為什么初始化的代碼是無鎖狀態(tài),進(jìn)入同步塊產(chǎn)生競爭就繞過偏向鎖直接變成輕量級鎖了呢?

    雖然默認(rèn)開啟了偏向鎖,但是開啟有延遲,大概 4s。原因是 JVM 內(nèi)部的代碼有很多地方用到了synchronized,如果直接開啟偏向,產(chǎn)生競爭就要有鎖升級,會帶來額外的性能損耗,所以就有了延遲策略


    我們可以通過參數(shù)?-XX:BiasedLockingStartupDelay=0?將延遲改為0,但是不建議這么做。我們可以通過一張圖來理解一下目前的情況:

    場景2

    那我們就代碼延遲 5 秒來創(chuàng)建對象,來看看偏向是否生效

    public?static?void?main(String[]?args)?throws?InterruptedException?{//?睡眠?5sThread.sleep(5000);Object?o?=?new?Object();log.info("未進(jìn)入同步塊,MarkWord 為:");log.info(ClassLayout.parseInstance(o).toPrintable());synchronized?(o){log.info(("進(jìn)入同步塊,MarkWord 為:"));log.info(ClassLayout.parseInstance(o).toPrintable());}}

    重新查看運(yùn)行結(jié)果:


    這樣的結(jié)果是符合我們預(yù)期的,但是結(jié)果中的?biasable?狀態(tài),在 MarkWord 表格中并不存在,其實(shí)這是一種匿名偏向狀態(tài),是對象初始化中,JVM 幫我們做的

    這樣當(dāng)有線程進(jìn)入同步塊:

  • 可偏向狀態(tài):直接就 CAS 替換 ThreadID,如果成功,就可以獲取偏向鎖了

  • 不可偏向狀態(tài):就會變成輕量級鎖

  • 那問題又來了,現(xiàn)在鎖對象有具體偏向的線程,如果新的線程過來執(zhí)行同步塊會偏向新的線程嗎?

    場景3

    public?static?void?main(String[]?args)?throws?InterruptedException?{//?睡眠?5sThread.sleep(5000);Object?o?=?new?Object();log.info("未進(jìn)入同步塊,MarkWord 為:");log.info(ClassLayout.parseInstance(o).toPrintable());synchronized?(o){log.info(("進(jìn)入同步塊,MarkWord 為:"));log.info(ClassLayout.parseInstance(o).toPrintable());}Thread?t2?=?new?Thread(()?->?{synchronized?(o)?{log.info("新線程獲取鎖,MarkWord為:");log.info(ClassLayout.parseInstance(o).toPrintable());}});t2.start();t2.join();log.info("主線程再次查看鎖對象,MarkWord為:");log.info(ClassLayout.parseInstance(o).toPrintable());synchronized?(o){log.info(("主線程再次進(jìn)入同步塊,MarkWord 為:"));log.info(ClassLayout.parseInstance(o).toPrintable());}}

    來看運(yùn)行結(jié)果,奇怪的事情發(fā)生了:


    • 標(biāo)記1: 初始可偏向狀態(tài)

    • 標(biāo)記2:偏向主線程后,主線程退出同步代碼塊

    • 標(biāo)記3: ?新線程進(jìn)入同步代碼塊,升級成了輕量級鎖

    • 標(biāo)記4: 新線程輕量級鎖退出同步代碼塊,主線程查看,變?yōu)椴豢善驙顟B(tài)

    • 標(biāo)記5: 由于對象不可偏向,同場景1主線程再次進(jìn)入同步塊,自然就會用輕量級鎖

    至此,場景一二三可以總結(jié)為一張圖:


    從這樣的運(yùn)行結(jié)果上來看,偏向鎖像是“一錘子買賣”,只要偏向了某個線程,后續(xù)其他線程嘗試獲取鎖,都會變?yōu)檩p量級鎖,這樣的偏向非常有局限性。事實(shí)上并不是這樣,如果你仔細(xì)看標(biāo)記2(已偏向狀態(tài)),還有個 epoch 我們沒有提及,這個值就是打破這種局限性的關(guān)鍵,在了解 epoch 之前,我們還要了解一個概念——偏向撤銷

    偏向撤銷

    在真正講解偏向撤銷之前,需要和大家明確一個概念——偏向鎖撤銷和偏向鎖釋放是兩碼事

  • 撤銷:籠統(tǒng)的說就是多個線程競爭導(dǎo)致不能再使用偏向模式的時候,主要是告知這個鎖對象不能再用偏向模式

  • 釋放:和你的常規(guī)理解一樣,對應(yīng)的就是 synchronized 方法的退出或 synchronized 塊的結(jié)束

  • 何為偏向撤銷?

    從偏向狀態(tài)撤回原有的狀態(tài),也就是將 MarkWord 的第 3 位(是否偏向撤銷)的值,從 1 變回 0

    如果只是一個線程獲取鎖,再加上「偏心」的機(jī)制,是沒有理由撤銷偏向的,所以偏向的撤銷只能發(fā)生在有競爭的情況下

    想要撤銷偏向鎖,還不能對持有偏向鎖的線程有影響,所以就要等待持有偏向鎖的線程到達(dá)一個?safepoint 安全點(diǎn)(這里的安全點(diǎn)是 JVM 為了保證在垃圾回收的過程中引用關(guān)系不會發(fā)生變化設(shè)置的一種安全狀態(tài),在這個狀態(tài)上會暫停所有線程工作), 在這個安全點(diǎn)會掛起獲得偏向鎖的線程

    在這個安全點(diǎn),線程可能還是處在不同狀態(tài)的,先說結(jié)論(因?yàn)樵创a就是這么寫的,可能有疑惑的地方會在后面解釋)

  • 線程不存活或者活著的線程但退出了同步塊,很簡單,直接撤銷偏向就好了

  • 活著的線程但仍在同步塊之內(nèi),那就要升級成輕量級鎖

  • 這個和 epoch 貌似還是沒啥關(guān)系,因?yàn)檫@還不是全部場景。偏向鎖是特定場景下提升程序效率的方案,可并不代表程序員寫的程序都滿足這些特定場景,比如這些場景(在開啟偏向鎖的前提下):

  • 一個線程創(chuàng)建了大量對象并執(zhí)行了初始的同步操作,之后在另一個線程中將這些對象作為鎖進(jìn)行之后的操作。這種case下,會導(dǎo)致大量的偏向鎖撤銷操作

  • 明知有多線程競爭(生產(chǎn)者/消費(fèi)者隊(duì)列),還要使用偏向鎖,也會導(dǎo)致各種撤銷

  • 很顯然,這兩種場景肯定會導(dǎo)致偏向撤銷的,一個偏向撤銷的成本無所謂,大量偏向撤銷的成本是不能忽視的。那怎么辦?既不想禁用偏向鎖,還不想忍受大量撤銷偏向增加的成本,這種方案就是設(shè)計(jì)一個有階梯的底線

    批量重偏向(bulk rebias)

    這是第一種場景的快速解決方案,以 class 為單位,為每個 class 維護(hù)一個偏向鎖撤銷計(jì)數(shù)器,每一次該class的對象發(fā)生偏向撤銷操作時,該計(jì)數(shù)器?+1,當(dāng)這個值達(dá)到重偏向閾值(默認(rèn)20)時:

    BiasedLockingBulkRebiasThreshold?=?20

    JVM 就認(rèn)為該class的偏向鎖有問題,因此會進(jìn)行批量重偏向, 它的實(shí)現(xiàn)方式就用到了我們上面說的?epoch

    Epoch,如其含義「紀(jì)元」一樣,就是一個時間戳。每個 class 對象會有一個對應(yīng)的epoch字段,每個處于偏向鎖狀態(tài)對象的mark word?中也有該字段,其初始值為創(chuàng)建該對象時 class 中的epoch的值(此時二者是相等的)。每次發(fā)生批量重偏向時,就將該值加1,同時遍歷JVM中所有線程的棧

  • 找到該 class 所有正處于加鎖狀態(tài)的偏向鎖對象,將其epoch字段改為新值

  • class 中不處于加鎖狀態(tài)的偏向鎖對象(沒被任何線程持有,但之前是被線程持有過的,這種鎖對象的 markword 肯定也是有偏向的),保持?epoch?字段值不變

  • 這樣下次獲得鎖時,發(fā)現(xiàn)當(dāng)前對象的epoch值和class的epoch,本著今朝不問前朝事?的原則(上一個紀(jì)元),那就算當(dāng)前已經(jīng)偏向了其他線程,也不會執(zhí)行撤銷操作,而是直接通過 CAS 操作將其mark word的線程 ID 改成當(dāng)前線程 ID,這也算是一定程度的優(yōu)化,畢竟沒升級鎖;

    如果?epoch?都一樣,說明沒有發(fā)生過批量重偏向, 如果?markword?有線程ID,還有其他鎖來競爭,那鎖自然是要升級的(如同前面舉的例子 epoch=0)

    批量重偏向是第一階梯底線,還有第二階梯底線

    批量撤銷(bulk revoke)

    當(dāng)達(dá)到重偏向閾值后,假設(shè)該 class 計(jì)數(shù)器繼續(xù)增長,當(dāng)其達(dá)到批量撤銷的閾值后(默認(rèn)40)時,

    BiasedLockingBulkRevokeThreshold?=?40

    JVM就認(rèn)為該 class 的使用場景存在多線程競爭,會標(biāo)記該 class 為不可偏向。之后對于該 class 的鎖,直接走輕量級鎖的邏輯

    這就是第二階梯底線,但是在第一階梯到第二階梯的過渡過程中,也就是在徹底禁用偏向鎖之前,還給一次改過自新的機(jī)會,那就是另外一個計(jì)時器:

    BiasedLockingDecayTime?=?25000
  • 如果在距離上次批量重偏向發(fā)生的 25 秒之內(nèi),并且累計(jì)撤銷計(jì)數(shù)達(dá)到40,就會發(fā)生批量撤銷(偏向鎖徹底 game over)

  • 如果在距離上次批量重偏向發(fā)生超過 25 秒之外,那么就會重置在?[20, 40)?內(nèi)的計(jì)數(shù), 再給次機(jī)會

  • 大家有興趣可以寫代碼測試一下臨界點(diǎn),觀察鎖對象?markword?的變化

    至此,整個偏向鎖的工作流程可以用一張圖表示:


    到此,你應(yīng)該對偏向鎖有個基本的認(rèn)識了,但是我心中的好多疑問還沒有解除,咱們繼續(xù)看:

    HashCode 哪去了

    上面場景一,無鎖狀態(tài),對象頭中沒有 hashcode;偏向鎖狀態(tài),對象頭還是沒有 hashcode,那我們的 hashcode 哪去了?

    首先要知道,hashcode 不是創(chuàng)建對象就幫我們寫到對象頭中的,而是要經(jīng)過第一次調(diào)用?Object::hashCode()或者System::identityHashCode(Object)?才會存儲在對象頭中的。第一次生成的 hashcode后,該值應(yīng)該是一直保持不變的,但偏向鎖又是來回更改鎖對象的 markword,必定會對 hashcode 的生成有影響,那怎么辦呢?,我們來用代碼驗(yàn)證:

    場景一

    public?static?void?main(String[]?args)?throws?InterruptedException?{//?睡眠?5sThread.sleep(5000);Object?o?=?new?Object();log.info("未生成 hashcode,MarkWord 為:");log.info(ClassLayout.parseInstance(o).toPrintable());o.hashCode();log.info("已生成 hashcode,MarkWord 為:");log.info(ClassLayout.parseInstance(o).toPrintable());synchronized?(o){log.info(("進(jìn)入同步塊,MarkWord 為:"));log.info(ClassLayout.parseInstance(o).toPrintable());}}

    來看運(yùn)行結(jié)果


    結(jié)論就是:即便初始化為可偏向狀態(tài)的對象,一旦調(diào)用?Object::hashCode()?或者System::identityHashCode(Object)?,進(jìn)入同步塊就會直接使用輕量級鎖

    場景二

    假如已偏向某一個線程,然后生成 hashcode,然后同一個線程又進(jìn)入同步塊,會發(fā)生什么呢?來看代碼:

    public?static?void?main(String[]?args)?throws?InterruptedException?{//?睡眠?5sThread.sleep(5000);Object?o?=?new?Object();log.info("未生成 hashcode,MarkWord 為:");log.info(ClassLayout.parseInstance(o).toPrintable());synchronized?(o){log.info(("進(jìn)入同步塊,MarkWord 為:"));log.info(ClassLayout.parseInstance(o).toPrintable());}o.hashCode();log.info("生成?hashcode");synchronized?(o){log.info(("同一線程再次進(jìn)入同步塊,MarkWord 為:"));log.info(ClassLayout.parseInstance(o).toPrintable());}}

    查看運(yùn)行結(jié)果:


    結(jié)論就是:同場景一,會直接使用輕量級鎖

    場景三

    那假如對象處于已偏向狀態(tài),在同步塊中調(diào)用了那兩個方法會發(fā)生什么呢?繼續(xù)代碼驗(yàn)證:

    public?static?void?main(String[]?args)?throws?InterruptedException?{//?睡眠?5sThread.sleep(5000);Object?o?=?new?Object();log.info("未生成 hashcode,MarkWord 為:");log.info(ClassLayout.parseInstance(o).toPrintable());synchronized?(o){log.info(("進(jìn)入同步塊,MarkWord 為:"));log.info(ClassLayout.parseInstance(o).toPrintable());o.hashCode();log.info("已偏向狀態(tài)下,生成 hashcode,MarkWord 為:");log.info(ClassLayout.parseInstance(o).toPrintable());}}

    來看運(yùn)行結(jié)果:


    結(jié)論就是:如果對象處在已偏向狀態(tài),生成 hashcode 后,就會直接升級成重量級鎖

    最后用書中的一段話來描述 鎖和hashcode 之前的關(guān)系


    調(diào)用 Object.wait() 方法會發(fā)生什么?

    Object 除了提供了上述 hashcode 方法,還有?wait()?方法,這也是我們在同步塊中常用的,那這會對鎖產(chǎn)生哪些影響呢?來看代碼:

    public?static?void?main(String[]?args)?throws?InterruptedException?{//?睡眠?5sThread.sleep(5000);Object?o?=?new?Object();log.info("未生成 hashcode,MarkWord 為:");log.info(ClassLayout.parseInstance(o).toPrintable());synchronized?(o)?{log.info(("進(jìn)入同步塊,MarkWord 為:"));log.info(ClassLayout.parseInstance(o).toPrintable());log.info("wait?2s");o.wait(2000);log.info(("調(diào)用 wait 后,MarkWord 為:"));log.info(ClassLayout.parseInstance(o).toPrintable());}}

    查看運(yùn)行結(jié)果:


    結(jié)論就是,wait 方法是互斥量(重量級鎖)獨(dú)有的,一旦調(diào)用該方法,就會升級成重量級鎖(這個是面試可以說出的亮點(diǎn)內(nèi)容哦)

    最后再繼續(xù)豐富一下鎖對象變化圖:


    告別偏向鎖

    看到這個標(biāo)題你應(yīng)該是有些慌,為啥要告別偏向鎖,因?yàn)榫S護(hù)成本有些高了,來看 Open JDK 官方聲明,JEP 374: Deprecate and Disable Biased Locking,相信你看上面的文字說明也深有體會,為了一個現(xiàn)在少有的場景付出了巨大的代碼實(shí)現(xiàn)


    這個說明的更新時間距離現(xiàn)在很近,在 JDK15 版本就已經(jīng)開始了

    一句話解釋就是維護(hù)成本太高


    最終就是,JDK 15 之前,偏向鎖默認(rèn)是 enabled,從 15 開始,默認(rèn)就是 disabled,除非顯示的通過?UseBiasedLocking 開啟

    其中在 quarkus 上的一篇文章說明的更加直接


    偏向鎖給 JVM 增加了巨大的復(fù)雜性,只有少數(shù)非常有經(jīng)驗(yàn)的程序員才能理解整個過程,維護(hù)成本很高,大大阻礙了開發(fā)新特性的進(jìn)程(換個角度理解,你掌握了,是不是就是那少數(shù)有經(jīng)驗(yàn)的程序員了呢?哈哈)

    總結(jié)

    偏向鎖可能就這樣的走完了它的一生,有些同學(xué)可能直接發(fā)問,都被 deprecated 了,JDK都 17 了,還講這么多干什么?

  • java 任它發(fā),我用 Java8,這是很多主流的狀態(tài),至少你用的版本沒有被 deprecated

  • 面試還是會被經(jīng)常問到

  • 萬一哪天有更好的設(shè)計(jì)方案,“偏向鎖”又以新的形式回來了呢,了解變化才能更好理解背后設(shè)計(jì)

  • 奧卡姆剃刀原理,我們現(xiàn)實(shí)中的優(yōu)化也一樣,如果沒有必要不要增加實(shí)體,如果增加的內(nèi)容帶來很大的成本,不如大膽的廢除掉,接受一點(diǎn)落差

  • 之前對于偏向鎖我也只是單純的理論認(rèn)知,但是為了寫這篇文章,我翻閱了很多資料,包括也重新查看 Hotspot 源碼,說的這些內(nèi)容也并不能完全說明偏向鎖的整個流程細(xì)節(jié),還需要大家具體實(shí)踐追蹤查看,這里給出源碼的幾個關(guān)鍵入口,方便大家追蹤:

  • 偏向鎖入口:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/share/vm/interpreter/bytecodeInterpreter.cpp#l1816

  • 偏向撤銷入口:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/share/vm/interpreter/interpreterRuntime.cpp#l608

  • 偏向鎖釋放入口:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/share/vm/interpreter/bytecodeInterpreter.cpp#l1923

  • 文中有疑問的地方歡迎留言討論,有錯誤的地方還請大家?guī)兔χ刚?/p>

    靈魂追問

  • 輕量級和重量級鎖,hashcode 存在了什么位置?

  • 參考資料

    感謝各路前輩的精華總結(jié),可以讓我參考理解:

  • https://www.oracle.com/technetwork/java/javase/tech/biasedlocking-oopsla2006-preso-150106.pdf

  • https://www.oracle.com/technetwork/java/biasedlocking-oopsla2006-wp-149958.pdf

  • https://wiki.openjdk.java.net/display/HotSpot/Synchronization#Synchronization-Russel06

  • https://github.com/farmerjohngit/myblog/issues/12

  • https://zhuanlan.zhihu.com/p/440994983

  • https://mp.weixin.qq.com/s/G4z08HfiqJ4qm3th0KtovA

  • https://www.jianshu.com/p/884eb51266e4

  • 往期推薦

    “全宇宙首個”用中文編寫的操作系統(tǒng)!作者還自創(chuàng)了甲、乙、丙編程語言?


    Chrome 100發(fā)布:全新圖標(biāo),CPU、內(nèi)存占用暴降!


    本周 火火火火火 的開源項(xiàng)目


    有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)

    歡迎大家關(guān)注Java之道公眾號

    好文章,我在看??

    總結(jié)

    以上是生活随笔為你收集整理的synchronized 底层了解一下...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。