最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java synchronized最細(xì)講解

 更新時(shí)間:2021年09月10日 09:31:20   作者:Penguin——科波特  
synchronized是Java語(yǔ)言的關(guān)鍵字,當(dāng)它用來(lái)修飾一個(gè)方法或者一個(gè)代碼塊的時(shí)候,能夠保證在同一時(shí)刻最多只有一個(gè)線(xiàn)程執(zhí)行該段代碼。本文給大家介紹java中 synchronized的用法,對(duì)本文感興趣的朋友一起看看吧

前言

線(xiàn)程安全問(wèn)題的主要誘因有兩點(diǎn),一是存在共享數(shù)據(jù)(也稱(chēng)臨界資源),二是存在多條線(xiàn)程共同操作共享數(shù)據(jù)。

因此為了解決這個(gè)問(wèn)題,我們可能需要這樣一個(gè)方案,當(dāng)存在多個(gè)線(xiàn)程操作共享數(shù)據(jù)時(shí),需要保證同一時(shí)刻有且只有一個(gè)線(xiàn)程在操作共享數(shù)據(jù),其他線(xiàn)程必須等到該線(xiàn)程處理完數(shù)據(jù)后再進(jìn)行,這種方式有個(gè)高尚的名稱(chēng)叫互斥鎖,即能達(dá)到互斥訪(fǎng)問(wèn)目的的鎖,也就是說(shuō)當(dāng)一個(gè)共享數(shù)據(jù)被當(dāng)前正在訪(fǎng)問(wèn)的線(xiàn)程加上互斥鎖后,在同一個(gè)時(shí)刻,其他線(xiàn)程只能處于等待的狀態(tài),直到當(dāng)前線(xiàn)程處理完畢釋放該鎖。

在 Java 中,關(guān)鍵字 synchronized可以保證在同一個(gè)時(shí)刻,只有一個(gè)線(xiàn)程可以執(zhí)行某個(gè)方法或者某個(gè)代碼塊(主要是對(duì)方法或者代碼塊中存在共享數(shù)據(jù)的操作),同時(shí)我們還應(yīng)該注意到synchronized另外一個(gè)重要的作用,synchronized可保證一個(gè)線(xiàn)程的變化(主要是共享數(shù)據(jù)的變化)被其他線(xiàn)程所看到(保證可見(jiàn)性,完全可以替代Volatile功能),這點(diǎn)確實(shí)也是很重要的。

synchronized三種作用范圍(給對(duì)象加鎖)

在靜態(tài)方法上加鎖;

在非靜態(tài)方法上加鎖;

在代碼塊上加鎖;

public class SynchronizedSample {
 
    private final Object lock = new Object();
 
    private static int money = 0;
		//非靜態(tài)方法
    public synchronized void noStaticMethod(){
        money++;
    }
		//靜態(tài)方法
    public static synchronized void staticMethod(){
        money++;
    }
		
    public void codeBlock(){
      	//代碼塊
        synchronized (lock){
            money++;
        }
    }
}
作用范圍 鎖對(duì)象
非靜態(tài)方法 當(dāng)前對(duì)象 => this
靜態(tài)方法 類(lèi)對(duì)象 => SynchronizedSample.class (一切皆對(duì)象,這個(gè)是類(lèi)對(duì)象)
代碼塊 指定對(duì)象 => lock (以上面的代碼為例)

Synchronization實(shí)現(xiàn)原理

先理解Java對(duì)象頭與Monitor

1.對(duì)象頭:鎖的類(lèi)型和狀態(tài)和對(duì)象頭的Mark Word息息相關(guān);

在這里插入圖片描述

對(duì)象頭分為二個(gè)部分,Mard WordKlass Word

對(duì)象頭結(jié)構(gòu) 存儲(chǔ)信息-說(shuō)明
Mard Word 存儲(chǔ)對(duì)象的hashCode、鎖信息或分代年齡或GC標(biāo)志等信息
Klass Word 存儲(chǔ)指向?qū)ο笏鶎兕?lèi)(元數(shù)據(jù))的指針,JVM通過(guò)這個(gè)確定這個(gè)對(duì)象屬于哪個(gè)類(lèi)

其中Mark Word在默認(rèn)情況下存儲(chǔ)著對(duì)象的HashCode、分代年齡、鎖標(biāo)記位等以下是32位JVM的Mark Word默認(rèn)存儲(chǔ)結(jié)構(gòu)

鎖狀態(tài) 25bit 4bit 1bit是否是偏向鎖 2bit 鎖標(biāo)志位
無(wú)鎖狀態(tài) 對(duì)象HashCode 對(duì)象分代年齡 0 01

在這里插入圖片描述

主要分析一下重量級(jí)鎖也就是通常說(shuō)synchronized的對(duì)象鎖,鎖標(biāo)識(shí)位為10,其中指針指向的是monitor對(duì)象(也稱(chēng)為管程或監(jiān)視器鎖)的起始地址。每個(gè)對(duì)象都存在著一個(gè) monitor 與之關(guān)聯(lián),對(duì)象與其 monitor 之間的關(guān)系有存在多種實(shí)現(xiàn)方式,如monitor可以與對(duì)象一起創(chuàng)建銷(xiāo)毀或當(dāng)線(xiàn)程試圖獲取對(duì)象鎖時(shí)自動(dòng)生成,但當(dāng)一個(gè) monitor 被某個(gè)線(xiàn)程持有后,它便處于鎖定狀態(tài)。在Java虛擬機(jī)(HotSpot)中,monitor是由ObjectMonitor實(shí)現(xiàn)的,其主要數(shù)據(jù)結(jié)構(gòu)如下(位于HotSpot虛擬機(jī)源碼ObjectMonitor.hpp文件,C++實(shí)現(xiàn)的)

//👇圖詳細(xì)介紹重要變量的作用
ObjectMonitor() {
    _header       = NULL;
    _count        = 0;   // 重入次數(shù)
    _waiters      = 0,   // 等待線(xiàn)程數(shù)
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;  // 當(dāng)前持有鎖的線(xiàn)程
    _WaitSet      = NULL;  // 調(diào)用了 wait 方法的線(xiàn)程被阻塞 放置在這里
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ; // 等待鎖 處于block的線(xiàn)程 有資格成為候選資源的線(xiàn)程
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
  }

ObjectMonitor中有兩個(gè)隊(duì)列,_WaitSet 和 _EntryList,用來(lái)保存ObjectWaiter對(duì)象列表( 每個(gè)等待鎖的線(xiàn)程都會(huì)被封裝成ObjectWaiter對(duì)象),_owner指向持有ObjectMonitor對(duì)象的線(xiàn)程,當(dāng)多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)一段同步代碼時(shí),首先會(huì)進(jìn)入 _EntryList 集合,當(dāng)線(xiàn)程獲取到對(duì)象的monitor 后進(jìn)入 _Owner 區(qū)域并把monitor中的owner變量設(shè)置為當(dāng)前線(xiàn)程同時(shí)monitor中的計(jì)數(shù)器count加1,若線(xiàn)程調(diào)用 wait() 方法,將釋放當(dāng)前持有的monitor,owner變量恢復(fù)為null,count自減1,同時(shí)該線(xiàn)程進(jìn)入 WaitSet集合中等待被喚醒。若當(dāng)前線(xiàn)程執(zhí)行完畢也將釋放monitor(鎖)并復(fù)位變量的值,以便其他線(xiàn)程進(jìn)入獲取monitor(鎖)。如下圖所示

在這里插入圖片描述

由此看來(lái),monitor對(duì)象存在于每個(gè)Java對(duì)象的對(duì)象頭中(存儲(chǔ)的指針的指向),synchronized鎖便是通過(guò)這種方式獲取鎖的,也是為什么Java中任意對(duì)象可以作為鎖的原因,同時(shí)也是notify/notifyAll/wait等方法存在于頂級(jí)對(duì)象Object中的原因(關(guān)于這點(diǎn)稍后還會(huì)進(jìn)行分析)。

jdk6 之后做了改進(jìn),引入了偏向鎖和輕量級(jí)鎖:

  • 依賴(lài)底層操作系統(tǒng)的 mutex 相關(guān)指令實(shí)現(xiàn),加鎖解鎖需要在用戶(hù)態(tài)和內(nèi)核態(tài)之間切換,性能損耗非常明顯。
  • 研究人員發(fā)現(xiàn),大多數(shù)對(duì)象的加鎖和解鎖都是在特定的線(xiàn)程中完成。也就是出現(xiàn)線(xiàn)程競(jìng)爭(zhēng)鎖的情況概率比較低。他們做了一個(gè)實(shí)驗(yàn),找了一些典型的軟件,測(cè)試同一個(gè)線(xiàn)程加鎖解鎖的重復(fù)率,如下圖所示,可以看到重復(fù)加鎖比例非常高。早期JVM 有 19% 的執(zhí)行時(shí)間浪費(fèi)在鎖上。

1.無(wú)鎖到偏向鎖轉(zhuǎn)化的過(guò)程

在這里插入圖片描述

  • 首先A 線(xiàn)程訪(fǎng)問(wèn)同步代碼塊,使用CAS 操作將 Thread ID 放到 Mark Word 當(dāng)中;
  • 如果CAS 成功,此時(shí)線(xiàn)程A 就獲取了鎖
  • 如果線(xiàn)程CAS 失敗,證明有別的線(xiàn)程持有鎖,例如上圖的線(xiàn)程B 來(lái)CAS 就失敗的,這個(gè)時(shí)候啟動(dòng)偏向鎖撤銷(xiāo) (revoke bias);
  • 鎖撤銷(xiāo)流程:
    • 讓 A線(xiàn)程在全局安全點(diǎn)阻塞(類(lèi)似于GC前線(xiàn)程在安全點(diǎn)阻塞)
    • 遍歷線(xiàn)程棧,查看是否有被鎖對(duì)象的鎖記錄( Lock Record),如果有Lock Record,需要修復(fù)鎖記錄和Markword,使其變成無(wú)鎖狀態(tài)。
    • 恢復(fù)A線(xiàn)程
    • 將是否為偏向鎖狀態(tài)置為 0 ,開(kāi)始進(jìn)行輕量級(jí)加鎖流程

2.偏向鎖升級(jí)輕量級(jí)

  • 線(xiàn)程在自己的棧楨中創(chuàng)建鎖記錄 LockRecord。
  • 線(xiàn)程A 將 Mark Word 拷貝到線(xiàn)程棧的 Lock Record中
  • 將鎖記錄中的Owner指針指向加鎖的對(duì)象(存放對(duì)象地址)
  • 將鎖對(duì)象的對(duì)象頭的MarkWord替換為指向鎖記錄的指針。
  • 這時(shí)鎖標(biāo)志位變成 00 ,表示輕量級(jí)鎖

其實(shí)就是撤銷(xiāo)偏向鎖后,當(dāng)前線(xiàn)程棧中會(huì)分配鎖記錄,并拷貝Mark Word到鎖記錄中。然后兩個(gè)線(xiàn)程用CAS的方式去修改Mark Word中的指針指向自己,假如說(shuō)第一個(gè)線(xiàn)程修改成功了,然后將鎖升級(jí)為輕量級(jí)鎖,去執(zhí)行同步語(yǔ)句塊中的內(nèi)容。

3.輕量級(jí)到重量級(jí)

修改失敗的第二個(gè)線(xiàn)程會(huì)進(jìn)入自旋狀態(tài),自旋結(jié)束后會(huì)繼續(xù)去嘗試CAS修改指針指向自己。如果自旋失敗超過(guò)一定次數(shù)的時(shí)候(這個(gè)次數(shù)會(huì)動(dòng)態(tài)進(jìn)行調(diào)整),會(huì)請(qǐng)求JVM將此時(shí)的鎖狀態(tài)升級(jí)為重量級(jí)鎖,這是依賴(lài)于底層操作系統(tǒng)的調(diào)度庫(kù)來(lái)實(shí)現(xiàn)的。接著將Mark Word指向重量級(jí)鎖Monitor的指針,然后掛起當(dāng)前第二個(gè)線(xiàn)程(被放在Monitor的_EntryList中)。等一個(gè)線(xiàn)程執(zhí)行完畢后,會(huì)查看當(dāng)前Mark Word中的指針是否仍然指向自己,如果是自己的話(huà)就釋放鎖,否則不是自己的話(huà),說(shuō)明此時(shí)已經(jīng)升級(jí)成了重量級(jí)鎖,除了釋放鎖之后,還會(huì)喚醒阻塞的線(xiàn)程,進(jìn)行新一輪的鎖競(jìng)爭(zhēng)。在此之后,該鎖就一直會(huì)是重量級(jí)鎖存在了

ps:為什么設(shè)計(jì)自旋數(shù)超過(guò)一定限制設(shè)置為重量級(jí)鎖?

一般來(lái)說(shuō),同步代碼塊內(nèi)的代碼應(yīng)該很快就執(zhí)行結(jié)束,這時(shí)候修改失敗的第二個(gè)線(xiàn)程自旋一段時(shí)間是很容易拿到鎖的,但是如果不巧,沒(méi)拿到,自旋其實(shí)就是死循環(huán),很耗CPU的,因此就直接轉(zhuǎn)成重量級(jí)鎖咯,這樣就不用了線(xiàn)程一直自旋了。

源碼才學(xué)疏淺只了解到:

synchronized 在代碼塊上是通過(guò) monitorenter 和 monitorexit指令實(shí)現(xiàn),在靜態(tài)方法和 方法上加鎖是在方法的flags 中加入 ACC_SYNCHRONIZED 。JVM 運(yùn)行方法時(shí)檢查方法的flags,遇到同步標(biāo)識(shí)開(kāi)始啟動(dòng)前面的加鎖流程,在方法內(nèi)部遇到monitorenter指令開(kāi)始加鎖。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • Java代碼實(shí)現(xiàn)Map和Object互轉(zhuǎn)及Map和Json互轉(zhuǎn)

    Java代碼實(shí)現(xiàn)Map和Object互轉(zhuǎn)及Map和Json互轉(zhuǎn)

    這篇文章主要介紹了Java代碼實(shí)現(xiàn)map和Object互轉(zhuǎn)及Map和json互轉(zhuǎn)的相關(guān)資料,需要的朋友可以參考下
    2016-05-05
  • java插入排序和希爾排序?qū)崿F(xiàn)思路及代碼

    java插入排序和希爾排序?qū)崿F(xiàn)思路及代碼

    這篇文章主要介紹了插入排序和希爾排序兩種排序算法,文章通過(guò)代碼示例和圖解詳細(xì)介紹了這兩種排序算法的實(shí)現(xiàn)過(guò)程和原理,需要的朋友可以參考下
    2025-03-03
  • Java計(jì)算程序代碼執(zhí)行時(shí)間的方法小結(jié)

    Java計(jì)算程序代碼執(zhí)行時(shí)間的方法小結(jié)

    這篇文章主要介紹了Java計(jì)算程序代碼執(zhí)行時(shí)間的方法,結(jié)合實(shí)例形式總結(jié)分析了java采用毫秒數(shù)及納秒數(shù)計(jì)算程序運(yùn)行時(shí)間的相關(guān)操作技巧,需要的朋友可以參考下
    2017-11-11
  • Springboot使用SPI注冊(cè)bean到spring容器的示例代碼

    Springboot使用SPI注冊(cè)bean到spring容器的示例代碼

    這篇文章主要介紹了Springboot使用SPI注冊(cè)bean到spring容器,主要包括mydriver接口,mysqldriver實(shí)現(xiàn)過(guò)程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-10-10
  • 淺談Java轉(zhuǎn)義符\\|

    淺談Java轉(zhuǎn)義符\\|

    java中\(zhòng)需要用\\來(lái)表示吧這個(gè)你應(yīng)該知道,而split中傳入的參數(shù)是什么呢 他并不是普通的字符串 你可以查一下api文檔 它要求傳入的是正則表達(dá)式 而正則表達(dá)式也需要這個(gè)\所以在這里有這個(gè)\\
    2015-06-06
  • Spring依賴(lài)注入Dependency Injection的三種方式

    Spring依賴(lài)注入Dependency Injection的三種方式

    依賴(lài)注入(Dependency Injection)和控制反轉(zhuǎn)(Inversion of Control)是同一個(gè)概念。具體含義是:當(dāng)某個(gè)角色(可能是一個(gè)Java實(shí)例,調(diào)用者)需要另一個(gè)角色(另一個(gè)Java實(shí)例,被調(diào)用者)的協(xié)助時(shí),在傳統(tǒng)的程序設(shè)計(jì)過(guò)程中,通常由調(diào)用者來(lái)創(chuàng)建被調(diào)用者的實(shí)例
    2023-02-02
  • Java的回調(diào)機(jī)制實(shí)例詳解

    Java的回調(diào)機(jī)制實(shí)例詳解

    這篇文章主要介紹了Java的回調(diào)機(jī)制,結(jié)合實(shí)例形式詳細(xì)分析了java回調(diào)機(jī)制相關(guān)原理、用法及操作注意事項(xiàng),需要的朋友可以參考下
    2019-08-08
  • Spring中BeanFactory與FactoryBean接口的區(qū)別詳解

    Spring中BeanFactory與FactoryBean接口的區(qū)別詳解

    這篇文章主要給大家介紹了關(guān)于Spring中BeanFactory與FactoryBean接口的區(qū)別的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用Spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • spring-kafka使消費(fèi)者動(dòng)態(tài)訂閱新增的topic問(wèn)題

    spring-kafka使消費(fèi)者動(dòng)態(tài)訂閱新增的topic問(wèn)題

    這篇文章主要介紹了spring-kafka使消費(fèi)者動(dòng)態(tài)訂閱新增的topic問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Java自學(xué)書(shū)籍推薦 程序員到架構(gòu)師必看的書(shū)

    Java自學(xué)書(shū)籍推薦 程序員到架構(gòu)師必看的書(shū)

    這篇文章主要為大家推薦了Java程序員到架構(gòu)師自學(xué)書(shū)籍,幫助大家不斷提高自己的專(zhuān)業(yè)水平,感興趣的小伙伴們可以參考一下
    2016-09-09

最新評(píng)論

SHOW| 新邵县| 腾冲县| 金山区| 兴安盟| 佛教| 青川县| 莱芜市| 新巴尔虎右旗| 卓尼县| 晋宁县| 灵寿县| 安达市| 南岸区| 晋州市| 枞阳县| 濮阳市| 栾川县| 枞阳县| 滨海县| 平湖市| 新乐市| 海口市| 洛阳市| 庆安县| 岳普湖县| 娄烦县| 富平县| 潜山县| 怀宁县| 聊城市| 吴忠市| 建湖县| 泾源县| 平和县| 乐山市| 长丰县| 贡山| 和林格尔县| 景宁| 浮梁县|