日B视频 亚洲,啪啪啪网站一区二区,91色情精品久久,日日噜狠狠色综合久,超碰人妻少妇97在线,999青青视频,亚洲一区二卡,让本一区二区视频,日韩网站推荐

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Java并發(fā)編程中線程同步的常用手段synchronized用法

FPGA之家 ? 來(lái)源:sowhat1412 ? 作者:sowhat1412 ? 2021-04-04 11:30 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

synchronized關(guān)鍵字是Java并發(fā)編程中線程同步的常用手段之一,其作用有三個(gè):

互斥性:確保線程互斥的訪問(wèn)同步代,鎖自動(dòng)釋放,多個(gè)線程操作同個(gè)代碼塊或函數(shù)必須排隊(duì)獲得鎖,

可見性:保證共享變量的修改能夠及時(shí)可見,獲得鎖的線程操作完畢后會(huì)將所數(shù)據(jù)刷新到共享內(nèi)存區(qū)[1]

有序性:不解決重排序,但保證有序性

synchronized用法有三個(gè):

修飾實(shí)例方法

修飾靜態(tài)方法

修飾代碼塊

1. 修飾實(shí)例方法

synchronized關(guān)鍵詞作用在方法的前面,用來(lái)鎖定方法,其實(shí)默認(rèn)鎖定的是this對(duì)象。

publicclassThread1implementsRunnable{ //共享資源(臨界資源)staticinti=0; //如果沒(méi)有synchronized關(guān)鍵字,輸出小于20000publicsynchronizedvoidincrease(){ i++; } publicvoidrun(){ for(intj=0;j<10000;j++){ ????????????increase(); ????????} ????} ????public?static?void?main(String[]?args)?throws?InterruptedException?{ ????????Thread1?t=new?Thread1(); ????????Thread?t1=new?Thread(t); ????????Thread?t2=new?Thread(t); ????????t1.start(); ????????t2.start(); ????????t1.join();//主線程等待t1執(zhí)行完畢????????t2.join();//主線程等待t2執(zhí)行完畢????????System.out.println(i); ????} }

2a18f1b6-8ecb-11eb-8b86-12bb97331649.png


2. 修飾靜態(tài)方法

synchronized還是修飾在方法上,不過(guò)修飾的是靜態(tài)方法,等價(jià)于鎖定的是Class對(duì)象,

publicclassThread1{ //共享資源(臨界資源)staticinti=0; //如果沒(méi)有synchronized關(guān)鍵字,輸出小于20000publicstaticsynchronizedvoidincrease(){ i++; } publicstaticvoidmain(String[]args)throwsInterruptedException{ Threadt1=newThread(newRunnable(){ publicvoidrun(){ for(intj=0;j

2a18f1b6-8ecb-11eb-8b86-12bb97331649.png


3. 修飾代碼塊

用法是在函數(shù)體內(nèi)部對(duì)于要修改的參數(shù)區(qū)間用synchronized來(lái)修飾,相比與鎖定函數(shù)這個(gè)范圍更小,可以指定鎖定什么對(duì)象。

publicclassThread1implementsRunnable{ //共享資源(臨界資源)staticinti=0; @Overridepublicvoidrun(){ for(intj=0;j

2a18f1b6-8ecb-11eb-8b86-12bb97331649.png

總結(jié):

synchronized修飾的實(shí)例方法,多線程并發(fā)訪問(wèn)時(shí),只能有一個(gè)線程進(jìn)入,獲得對(duì)象內(nèi)置鎖,其他線程阻塞等待,但在此期間線程仍然可以訪問(wèn)其他方法。

synchronized修飾的靜態(tài)方法,多線程并發(fā)訪問(wèn)時(shí),只能有一個(gè)線程進(jìn)入,獲得類鎖,其他線程阻塞等待,但在此期間線程仍然可以訪問(wèn)其他方法。

synchronized修飾的代碼塊,多線程并發(fā)訪問(wèn)時(shí),只能有一個(gè)線程進(jìn)入,根據(jù)括號(hào)中的對(duì)象或者是類,獲得相應(yīng)的對(duì)象內(nèi)置鎖或者是類鎖

每個(gè)類都有一個(gè)類鎖,類的每個(gè)對(duì)象也有一個(gè)內(nèi)置鎖,它們是互不干擾的,也就是說(shuō)一個(gè)線程可以同時(shí)獲得類鎖和該類實(shí)例化對(duì)象的內(nèi)置鎖,當(dāng)線程訪問(wèn)非synchronzied修飾的方法時(shí),并不需要獲得鎖,因此不會(huì)產(chǎn)生阻塞。

管程

管程[2](英語(yǔ):Monitors,也稱為監(jiān)視器) 在操作系統(tǒng)中是很重要的概念,管程其實(shí)指的是管理共享變量以及管理共享變量的操作過(guò)程。有點(diǎn)扮演中介的意思,管程管理一堆對(duì)象,多個(gè)線程同一時(shí)候只能有一個(gè)線程來(lái)訪問(wèn)這些東西。

管程可以看做一個(gè)軟件模塊,它是將共享的變量和對(duì)于這些共享變量的操作封裝起來(lái),形成一個(gè)具有一定接口的功能模塊,進(jìn)程可以調(diào)用管程來(lái)實(shí)現(xiàn)進(jìn)程級(jí)別的并發(fā)控制。

進(jìn)程只能互斥的使用管程,即當(dāng)一個(gè)進(jìn)程使用管程時(shí),另一個(gè)進(jìn)程必須等待。當(dāng)一個(gè)進(jìn)程使用完管程后,它必須釋放管程并喚醒等待管程的某一個(gè)進(jìn)程。

管程解決互斥問(wèn)題相對(duì)簡(jiǎn)單,把共享變量以及共享變量的操作都封裝在一個(gè)類中

2aa786c4-8ecb-11eb-8b86-12bb97331649.png

當(dāng)線程A和線程B需要獲取共享變量count時(shí),就需要調(diào)用get和set方法,而get和set方法則保證互斥性,保證每次只能有一個(gè)線程訪問(wèn)。

生活中舉例管程比如鏈家店長(zhǎng)分配給每一個(gè)中介管理一部分二手房源,多個(gè)客戶通過(guò)中介進(jìn)行房屋買賣。

中介 就是管程。

多個(gè)二手房源被一個(gè)中介管理中,就是一個(gè)管程管理著多個(gè)系統(tǒng)資源。

多個(gè)客戶就相當(dāng)于多個(gè)線程。

Synchronzied的底層原理

對(duì)象頭解析

我們知道在Java的JVM內(nèi)存區(qū)域[3]中一個(gè)對(duì)象在堆區(qū)創(chuàng)建,創(chuàng)建后的對(duì)象由三部分組成。

2ae9941a-8ecb-11eb-8b86-12bb97331649.png

這三部分功能如下:

填充數(shù)據(jù):由于虛擬機(jī)要求對(duì)象起始地址必須是8字節(jié)的整數(shù)倍。填充數(shù)據(jù)不是必須存在的,僅僅是為了字節(jié)對(duì)齊。

實(shí)例變量:存放類的屬性數(shù)據(jù)信息,包括父類的屬性信息,這部分內(nèi)存按4字節(jié)對(duì)齊。

對(duì)象頭:主要包括兩部分Klass Point跟Mark Word

Klass Point(類型指針):是對(duì)象指向它的類元數(shù)據(jù)的指針,虛擬機(jī)通過(guò)這個(gè)指針來(lái)確定這個(gè)對(duì)象是哪個(gè)類的實(shí)例。

Mark Word(標(biāo)記字段):這一部分用于儲(chǔ)存對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù),如哈希碼,GC分代年齡,鎖狀態(tài)標(biāo)志,鎖指針等,這部分?jǐn)?shù)據(jù)在32bit和64bit的虛擬機(jī)中大小分別為32bit和64bit,考慮到虛擬機(jī)的空間效率,Mark Word被設(shè)計(jì)成一個(gè)非固定的數(shù)據(jù)結(jié)構(gòu)以便在極小的空間中存儲(chǔ)盡量多的信息,它會(huì)根據(jù)對(duì)象的狀態(tài)復(fù)用自己的存儲(chǔ)空間(跟ConcurrentHashMap里的標(biāo)志位類似),詳細(xì)情況如下圖:

Mark Word狀態(tài)表示位如下:

2af13148-8ecb-11eb-8b86-12bb97331649.png

synchronized不論是修飾方法還是代碼塊,都是通過(guò)持有修飾對(duì)象的鎖來(lái)實(shí)現(xiàn)同步,synchronized鎖對(duì)象是存在對(duì)象頭Mark Word。其中輕量級(jí)鎖和偏向鎖是Java6對(duì)synchronized鎖進(jìn)行優(yōu)化后新增加的,這里我們主要分析一下重量級(jí)鎖也就是通常說(shuō)synchronized的對(duì)象鎖,鎖標(biāo)識(shí)位為10,其中指針指向的是monitor對(duì)象(也稱為管程或監(jiān)視器鎖)的起始地址。每個(gè)對(duì)象都存在著一個(gè)monitor[4]與之關(guān)聯(lián)。

2b297c10-8ecb-11eb-8b86-12bb97331649.png


匯編查看

分析對(duì)象的monitor前我們先通過(guò)反匯編看下同步方法跟同步方法塊在匯編語(yǔ)言級(jí)別是什么樣的指令。

publicclassSynchronizedTest{ publicsynchronizedvoiddoSth(){ System.out.println("HelloWorld"); } publicvoiddoSth1(){ synchronized(SynchronizedTest.class){ System.out.println("HelloWorld"); } } }

javac SynchronizedTest .java然后javap -c SynchronizedTest反編譯后看匯編指令如下:

publicsynchronizedvoiddoSth(); descriptor:()V flags:ACC_PUBLIC,ACC_SYNCHRONIZED//這是重點(diǎn)方法鎖Code: stack=2,locals=1,args_size=10:getstatic#2 3:ldc#3 5:invokevirtual#4 8:returnpublicvoiddoSth1(); descriptor:()V flags:ACC_PUBLIC Code: stack=2,locals=3,args_size=10:ldc#5 2:dup 3:astore_1 4:monitorenter//進(jìn)入同步方法5:getstatic#2 8:ldc#3 10:invokevirtual#4 13:aload_1 14:monitorexit//正常時(shí)退出同步方法15:goto2318:astore_2 19:aload_1 20:monitorexit//異常時(shí)退出同步方法21:aload_2 22:athrow 23:return

我們可以看到Java編譯器為我們生成的字節(jié)碼。在對(duì)于doSth和doSth1的處理上稍有不同。也就是說(shuō)。JVM對(duì)于同步方法和同步代碼塊的處理方式不同。對(duì)于同步方法,JVM采用ACC_SYNCHRONIZED標(biāo)記符來(lái)實(shí)現(xiàn)同步。對(duì)于同步代碼塊。JVM采用monitorenter、monitorexit兩個(gè)指令來(lái)實(shí)現(xiàn)同步。

ACC_SYNCHRONIZED

方法級(jí)的同步是隱式的。同步方法的常量池中會(huì)有一個(gè)ACC_SYNCHRONIZED標(biāo)志。當(dāng)某個(gè)線程要訪問(wèn)某個(gè)方法的時(shí)候,會(huì)檢查是否有ACC_SYNCHRONIZED,如果有設(shè)置,則需要先獲得監(jiān)視器鎖,然后開始執(zhí)行方法,方法執(zhí)行之后再釋放監(jiān)視器鎖。這時(shí)如果其他線程來(lái)請(qǐng)求執(zhí)行方法,會(huì)因?yàn)闊o(wú)法獲得監(jiān)視器鎖而被阻斷住。值得注意的是,如果在方法執(zhí)行過(guò)程中,發(fā)生了異常,并且方法內(nèi)部并沒(méi)有處理該異常,那么在異常被拋到方法外面之前監(jiān)視器鎖會(huì)被自動(dòng)釋放。

monitorenter跟monitorexit

可以把執(zhí)行monitorenter指令理解為加鎖,執(zhí)行monitorexit理解為釋放鎖。每個(gè)對(duì)象維護(hù)著一個(gè)記錄著被鎖次數(shù)的計(jì)數(shù)器。未被鎖定的對(duì)象的該計(jì)數(shù)器為0,當(dāng)一個(gè)線程獲得鎖(執(zhí)行monitorenter)后,該計(jì)數(shù)器自增變?yōu)?1 ,當(dāng)同一個(gè)線程再次獲得該對(duì)象的鎖的時(shí)候,計(jì)數(shù)器再次自增。當(dāng)同一個(gè)線程釋放鎖(執(zhí)行monitorexit指令)的時(shí)候,計(jì)數(shù)器再自減。當(dāng)計(jì)數(shù)器為0的時(shí)候。鎖將被釋放,其他線程便可以獲得鎖。

結(jié)論:同步方法和同步代碼塊底層都是通過(guò)monitor來(lái)實(shí)現(xiàn)同步的。兩者區(qū)別:同步方式是通過(guò)方法中的access_flags中設(shè)置ACC_SYNCHRONIZED標(biāo)志來(lái)實(shí)現(xiàn),同步代碼塊是通過(guò)monitorenter和monitorexit來(lái)實(shí)現(xiàn)。

monitor解析

每個(gè)對(duì)象都與一個(gè)monitor相關(guān)聯(lián),而monitor可以被線程擁有或釋放,在Java虛擬機(jī)(HotSpot)中,monitor是由ObjectMonitor實(shí)現(xiàn)的,其主要數(shù)據(jù)結(jié)構(gòu)如下(位于HotSpot虛擬機(jī)源碼ObjectMonitor.hpp文件,C++實(shí)現(xiàn)的)。

ObjectMonitor(){ _count=0;//記錄數(shù)_recursions=0;//鎖的重入次數(shù)_owner=NULL;//指向持有ObjectMonitor對(duì)象的線程_WaitSet=NULL;//調(diào)用wait后,線程會(huì)被加入到_WaitSet_EntryList=NULL;//等待獲取鎖的線程,會(huì)被加入到該列表}

monitor運(yùn)行圖如下:

2b7a949c-8ecb-11eb-8b86-12bb97331649.png

對(duì)于一個(gè)synchronized修飾的方法(代碼塊)來(lái)說(shuō):

當(dāng)多個(gè)線程同時(shí)訪問(wèn)該方法,那么這些線程會(huì)先被放進(jìn)_EntryList隊(duì)列,此時(shí)線程處于blocked狀態(tài)

當(dāng)一個(gè)線程獲取到了對(duì)象的monitor后,那么就可以進(jìn)入running狀態(tài),執(zhí)行方法塊,此時(shí),ObjectMonitor對(duì)象的_owner指向當(dāng)前線程,_count加1表示當(dāng)前對(duì)象鎖被一個(gè)線程獲取。

當(dāng)running狀態(tài)的線程調(diào)用wait()方法,那么當(dāng)前線程釋放monitor對(duì)象,進(jìn)入waiting狀態(tài),ObjectMonitor對(duì)象的_owner變?yōu)閚ull,_count減1,同時(shí)線程進(jìn)入_WaitSet隊(duì)列,直到有線程調(diào)用notify()方法喚醒該線程,則該線程進(jìn)入_EntryList隊(duì)列,競(jìng)爭(zhēng)到鎖再進(jìn)入_owner區(qū)。

如果當(dāng)前線程執(zhí)行完畢,那么也釋放monitor對(duì)象,ObjectMonitor對(duì)象的_owner變?yōu)閚ull,_count減1。

因?yàn)楸O(jiān)視器鎖(monitor)是依賴于底層的操作系統(tǒng)的Mutex Lock來(lái)實(shí)現(xiàn)的,而操作系統(tǒng)實(shí)現(xiàn)線程之間的切換時(shí)需要從用戶態(tài)轉(zhuǎn)換到核心態(tài)(具體可看CXUAN寫的OS哦),這個(gè)狀態(tài)之間的轉(zhuǎn)換需要相對(duì)比較長(zhǎng)的時(shí)間,時(shí)間成本相對(duì)較高,這也是早期的synchronized效率低的原因。慶幸在Java 6之后Java官方對(duì)從JVM層面對(duì)synchronized較大優(yōu)化最終提升顯著,Java 6之后,為了減少獲得鎖和釋放鎖所帶來(lái)的性能消耗,引入了鎖升級(jí)的概念。

鎖升級(jí)

synchronized鎖有四種狀態(tài),無(wú)鎖、偏向鎖、輕量級(jí)鎖、重量級(jí)鎖。這幾個(gè)狀態(tài)會(huì)隨著競(jìng)爭(zhēng)狀態(tài)逐漸升級(jí),鎖可以升級(jí)但不能降級(jí),但是偏向鎖狀態(tài)可以被重置為無(wú)鎖狀態(tài)??茖W(xué)性的說(shuō)這些鎖之前我們先看個(gè)簡(jiǎn)單通俗的例子來(lái)加深印象。

通俗說(shuō)各種鎖

偏向鎖、輕量級(jí)鎖和重量級(jí)鎖之間的關(guān)系,首先打個(gè)比方[5]:假設(shè)現(xiàn)在廁所只有一個(gè)位置,每個(gè)使用者都有打開門鎖的鑰匙。必須打開門鎖才能使用廁所。其中小明、小紅理解為兩個(gè)線程,上廁所理解為執(zhí)行同步代碼,門鎖理解為同步代碼的鎖

小明今天吃壞了東西需要反復(fù)去廁所,如果小明每次都要開鎖就很耽誤時(shí)間,于是門鎖將小明的臉記錄下來(lái)(假設(shè)那個(gè)鎖是智能鎖),下次小明再來(lái)的時(shí)候門鎖會(huì)自動(dòng)識(shí)別出是小明來(lái)了,然后自動(dòng)開鎖,這樣就省去了小明拿鑰匙開門的過(guò)程,此時(shí)門鎖就是偏向鎖,也可以理解為偏向小明的鎖。

接下來(lái),小紅又去上廁所,試圖將廁所的門鎖設(shè)置為偏向自己的偏向鎖,于是發(fā)現(xiàn)門鎖無(wú)法偏向自己,因?yàn)榇藭r(shí)門鎖已是偏向小明的偏向鎖。于是小紅很生氣,要求門鎖撤銷對(duì)小明的偏向,當(dāng)然,小明也不同意門鎖偏向小紅。于是等小明用完廁所之后,門鎖撤銷了對(duì)任何人的偏向(只要出現(xiàn)競(jìng)爭(zhēng)的情況,就會(huì)撤銷偏向鎖)。這個(gè)過(guò)程就是撤銷偏向鎖。此時(shí)門鎖升級(jí)為輕量級(jí)鎖。

等小明出來(lái)以后,輕量級(jí)鎖正式生效 。下一次小明和小紅同時(shí)來(lái)廁所,誰(shuí)跑的快誰(shuí)先走到門前,開門后將門鎖拿進(jìn)廁所,并將門鎖打開以后拿進(jìn)廁所里,將門反鎖,于是在門外原來(lái)放門鎖的位置放置了一個(gè)有人的標(biāo)志(這個(gè)標(biāo)識(shí)可以理解為指向門鎖的指針,或者理解為作為鎖的Java對(duì)象頭的Mark Word值),這時(shí),小紅看到有人以后很著急,催著里面的人出來(lái)時(shí)馬上進(jìn)去,于是不斷的來(lái)敲門,問(wèn)小明什么時(shí)候出來(lái)。這個(gè)過(guò)程就是自旋。

反復(fù)敲了幾次以后,小明受不了了,對(duì)小紅喊話,說(shuō)你別敲了,等我用完廁所我告訴你,于是小紅去一邊等著(線性阻塞)。此時(shí)門鎖升級(jí)為重量級(jí)鎖。升級(jí)為重量級(jí)鎖的后果就是,小紅不再反復(fù)敲門,小明在上完廁所以后必須告訴小紅一聲,否則小紅就會(huì)一直等著。

結(jié)論:

偏向鎖在只有一個(gè)人上廁所時(shí)非常高效,省去了開門的過(guò)程。輕量級(jí)鎖在有多人上廁所但是每個(gè)人使用的特別快的時(shí)候,比較高效,因?yàn)闀?huì)出現(xiàn)這種現(xiàn)象,小紅敲門的時(shí)候正好趕上小明出來(lái),這樣就省得小明出來(lái)告訴小紅以后小紅才能進(jìn)去,但是這樣可能會(huì)出現(xiàn)小紅敲門失敗的情況(就是敲門時(shí)小明還沒(méi)用完)。重量級(jí)鎖相比與輕量級(jí)鎖的多了一步小明呼喚小紅的步驟,但是卻省掉了小紅反復(fù)去敲門的過(guò)程,但是能保證小紅去廁所時(shí)廁所一定是沒(méi)人的。

偏向鎖

經(jīng)過(guò)HotSpot的作者大量的研究發(fā)現(xiàn)大多數(shù)時(shí)候是不存在鎖競(jìng)爭(zhēng)的,經(jīng)常是一個(gè)線程多次獲得同一個(gè)鎖,因此如果每次都要競(jìng)爭(zhēng)鎖會(huì)增大很多沒(méi)有必要付出的代價(jià),為了降低獲取鎖的代價(jià),才引入的偏向鎖。核心思想:

如果一個(gè)線程獲得了鎖,那么鎖就進(jìn)入偏向模式,此時(shí)Mark Word的結(jié)構(gòu)也變?yōu)槠蜴i結(jié)構(gòu),當(dāng)這個(gè)線程再次請(qǐng)求鎖時(shí),無(wú)需再做任何同步操作,即獲取鎖的過(guò)程,這樣就省去了大量有關(guān)鎖申請(qǐng)的操作,從而也就提供程序的性能。所以,對(duì)于沒(méi)有鎖競(jìng)爭(zhēng)的場(chǎng)合,偏向鎖有很好的優(yōu)化效果,畢竟極有可能連續(xù)多次是同一個(gè)線程申請(qǐng)相同的鎖。但是對(duì)于鎖競(jìng)爭(zhēng)比較激烈的場(chǎng)合,偏向鎖就失效了,因?yàn)檫@樣場(chǎng)合極有可能每次申請(qǐng)鎖的線程都是不相同的,因此這種場(chǎng)合下不應(yīng)該使用偏向鎖,否則會(huì)得不償失,需要注意的是,偏向鎖失敗后,并不會(huì)立即膨脹為重量級(jí)鎖,而是先升級(jí)為輕量級(jí)鎖。

具體流程:當(dāng)線程1訪問(wèn)代碼塊并獲取鎖對(duì)象時(shí),會(huì)在java對(duì)象頭和棧幀中記錄偏向的鎖的threadID,因?yàn)槠蜴i不會(huì)主動(dòng)釋放鎖,因此以后線程1再次獲取鎖的時(shí)候,需要比較當(dāng)前線程的threadID和Java對(duì)象頭中的threadID是否一致,如果一致(還是線程1獲取鎖對(duì)象),則無(wú)需使用CAS來(lái)加鎖、解鎖;如果不一致(其他線程,如線程2要競(jìng)爭(zhēng)鎖對(duì)象,而偏向鎖不會(huì)主動(dòng)釋放因此還是存儲(chǔ)的線程1的threadID),那么需要查看Java對(duì)象頭中記錄的線程1是否存活,如果沒(méi)有存活,那么鎖對(duì)象被重置為無(wú)鎖狀態(tài),其它線程(線程2)可以競(jìng)爭(zhēng)將其設(shè)置為偏向鎖;如果存活,那么立刻查找該線程(線程1)的棧幀信息,如果還是需要繼續(xù)持有這個(gè)鎖對(duì)象,那么暫停當(dāng)前線程1,撤銷偏向鎖,升級(jí)為輕量級(jí)鎖,如果線程1不再使用該鎖對(duì)象,那么將鎖對(duì)象狀態(tài)設(shè)為無(wú)鎖狀態(tài),重新偏向新的線程。

輕量級(jí)鎖

輕量級(jí)鎖考慮的是競(jìng)爭(zhēng)鎖對(duì)象的線程不多,而且線程持有鎖的時(shí)間也不長(zhǎng)的情景。因?yàn)樽枞€程需要高昂的耗時(shí)實(shí)現(xiàn)CPU從用戶態(tài)轉(zhuǎn)到內(nèi)核態(tài)的切換,如果剛剛阻塞不久這個(gè)鎖就被釋放了,那這個(gè)代價(jià)就有點(diǎn)得不償失了,因此這個(gè)時(shí)候就干脆不阻塞這個(gè)線程,讓它自旋這等待鎖釋放。

「原理跟升級(jí)」:線程1獲取輕量級(jí)鎖時(shí)會(huì)先把鎖對(duì)象的對(duì)象頭MarkWord復(fù)制一份到線程1的棧幀中創(chuàng)建的用于存儲(chǔ)鎖記錄的空間(稱為DisplacedMarkWord),然后使用CAS把對(duì)象頭中的內(nèi)容替換為線程1存儲(chǔ)的鎖記錄(DisplacedMarkWord)的地址;

如果在線程1復(fù)制對(duì)象頭的同時(shí)(在線程1CAS之前),線程2也準(zhǔn)備獲取鎖,復(fù)制了對(duì)象頭到線程2的鎖記錄空間中,但是在線程2CAS的時(shí)候,發(fā)現(xiàn)線程1已經(jīng)把對(duì)象頭換了,「線程2的CAS失敗,那么線程2就嘗試使用自旋鎖來(lái)等待線程1釋放鎖」。自旋鎖簡(jiǎn)單來(lái)說(shuō)就是讓線程2在循環(huán)中不斷CAS嘗試獲得鎖對(duì)象。

但是如果自旋的「時(shí)間太長(zhǎng)」也不行,因?yàn)樽孕且腃PU的,因此「自旋的次數(shù)是有限制」的,比如10次或者100次,如果自旋次數(shù)到了線程1還沒(méi)有釋放鎖,或者線程1還在執(zhí)行,線程2還在自旋等待,那么這個(gè)時(shí)候輕量級(jí)鎖就會(huì)膨脹為重量級(jí)鎖。重量級(jí)鎖把除了擁有鎖的線程都阻塞,防止CPU空轉(zhuǎn)。

2c079b44-8ecb-11eb-8b86-12bb97331649.png

鎖消除

消除鎖是虛擬機(jī)另外一種鎖的優(yōu)化,這種優(yōu)化更徹底,Java虛擬機(jī)在JIT編譯時(shí)通過(guò)對(duì)運(yùn)行上下文的掃描,去除不可能存在共享資源競(jìng)爭(zhēng)的鎖,通過(guò)這種方式消,除沒(méi)有必要的鎖,可以節(jié)省毫無(wú)意義的請(qǐng)求鎖時(shí)間,我們知道StringBuffer是線程安全的,里面包含鎖的存在,但是如果我們?cè)诤瘮?shù)內(nèi)部使用StringBuffer那么代碼會(huì)在JIT后會(huì)自動(dòng)將鎖釋放掉哦。

對(duì)比如下:

鎖狀態(tài) 優(yōu)點(diǎn) 缺點(diǎn) 適用場(chǎng)景
偏向鎖 加鎖解鎖無(wú)需額外消耗,跟非同步方法時(shí)間相差納秒級(jí)別 如果競(jìng)爭(zhēng)線程多,會(huì)帶來(lái)額外的鎖撤銷的消耗 基本沒(méi)有其他線程競(jìng)爭(zhēng)的同步場(chǎng)景
輕量級(jí)鎖 競(jìng)爭(zhēng)的線程不會(huì)阻塞而是在自旋,可提高程序響應(yīng)速度 如果一直無(wú)法獲得會(huì)自旋消耗CPU 少量線程競(jìng)爭(zhēng),持有鎖時(shí)間不長(zhǎng),追求響應(yīng)速度
重量級(jí)鎖 線程競(jìng)爭(zhēng)不會(huì)導(dǎo)致CPU自旋跟消耗CPU資源 線程阻塞,響應(yīng)時(shí)間長(zhǎng) 很多線程競(jìng)爭(zhēng)鎖,切鎖持有時(shí)間長(zhǎng),追求吞吐量時(shí)候

PS:ReentrantLock底層實(shí)現(xiàn)依賴于特殊的CPU指令,比如發(fā)送lock指令和unlock指令,不需要用戶態(tài)和內(nèi)核態(tài)的切換,所以效率高。而synchronized底層由監(jiān)視器鎖(monitor)是依賴于底層的操作系統(tǒng)的Mutex Lock需要用戶態(tài)和內(nèi)核態(tài)的切換,所以效率會(huì)低一些。

鎖升級(jí)流程圖

原文標(biāo)題:由淺入深逐步了解 Synchronized

文章出處:【微信公眾號(hào):FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

責(zé)任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 軟件
    +關(guān)注

    關(guān)注

    69

    文章

    5364

    瀏覽量

    91948
  • 編程
    +關(guān)注

    關(guān)注

    90

    文章

    3724

    瀏覽量

    97458

原文標(biāo)題:由淺入深逐步了解 Synchronized

文章出處:【微信號(hào):zhuyandz,微信公眾號(hào):FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    SGG-北京總部Java20250625-12月結(jié)課

    等生產(chǎn)環(huán)境事故時(shí),能夠迅速進(jìn)行性能調(diào)優(yōu)與故障排查,而非束手無(wú)策。同時(shí),課程重點(diǎn)攻克了多線程并發(fā)編程與 JUC(Java Util Concurrent)工具包的應(yīng)用,結(jié)合鎖機(jī)制、
    發(fā)表于 05-01 17:33

    [完結(jié)15章]Java轉(zhuǎn) AI高薪領(lǐng)域必備-從0到1打通生產(chǎn)級(jí)AI Agent開發(fā)

    的應(yīng)用,確保在極端高并發(fā)下,Java服務(wù)不會(huì)因?yàn)锳I接口的響應(yīng)過(guò)慢而導(dǎo)致線程池耗盡和系統(tǒng)雪崩。 三、 攻克核心堡壘:企業(yè)級(jí)RAG系統(tǒng)的深度定制 企業(yè)真正急需的不是通用的ChatGPT,而是基于私有
    發(fā)表于 04-30 13:46

    Java并發(fā)編程的“基石”——多線程概念初識(shí)

    。Java 的內(nèi)存可見性機(jī)制(如 happens-before 原則)能夠確保在極端并發(fā)下,任何一個(gè)調(diào)度節(jié)點(diǎn)看到的集群狀態(tài)都是一致且準(zhǔn)確的,從根本上杜絕了“腦裂”和資源超賣。 其次是 精妙的多線程協(xié)同
    發(fā)表于 04-16 18:50

    微課-掌握Java并發(fā)編程的“基石”,入門并發(fā)編程

    未來(lái)高并發(fā)系統(tǒng)底層邏輯:從入門掌握 Java 并發(fā)編程基石 在人類商業(yè)文明向數(shù)字化全面遷移的宏大進(jìn)程中,有一條隱秘但鐵一般的經(jīng)濟(jì)學(xué)法則正在被無(wú)限放大:在算力、存儲(chǔ)和網(wǎng)絡(luò)帶寬構(gòu)成的基礎(chǔ)設(shè)
    的頭像 發(fā)表于 04-14 14:25 ?119次閱讀

    Nginx高并發(fā)連接調(diào)優(yōu)實(shí)戰(zhàn)手冊(cè)

    Nginx 的高性能源自其事件驅(qū)動(dòng)架構(gòu)。與 Apache 的"每連接一線程"模型不同,Nginx 使用單線程事件循環(huán)處理數(shù)千個(gè)并發(fā)連接。理解這套架構(gòu)是調(diào)優(yōu)的前提。
    的頭像 發(fā)表于 03-16 15:28 ?490次閱讀

    解析Linux的進(jìn)程、線程和協(xié)程

    允許在單個(gè)線程內(nèi)實(shí)現(xiàn)多個(gè)協(xié)程的并發(fā)執(zhí)行。協(xié)程在執(zhí)行過(guò)程中可以主動(dòng)掛起和恢復(fù),這使得編寫高效的異步代碼變得更加容易。協(xié)程通常用于處理I/O密集型任務(wù),能夠提高程序的響應(yīng)性能。 協(xié)程的特點(diǎn)包括: (1
    發(fā)表于 12-22 11:00

    Linux多線程對(duì)比單線程的優(yōu)勢(shì)

    。「提高響應(yīng)性」:線程可以使程序更加響應(yīng)用戶輸入或其他事件,避免阻塞。線程相比單線程的優(yōu)點(diǎn):「并發(fā)性」:多線程可以同時(shí)執(zhí)行多個(gè)任務(wù),而單
    發(fā)表于 12-01 06:11

    飛凌嵌入式ElfBoard-文件I/O的了解探究之競(jìng)爭(zhēng)冒險(xiǎn)

    合適的同步機(jī)制和并發(fā)控制手段,例如使用互斥鎖、信號(hào)量、條件變量等來(lái)確保對(duì)共享資源的安全訪問(wèn)和修改,以保證程序的正確性和可靠性。
    發(fā)表于 11-26 15:38

    為什么需要中線安防保護(hù)器?

    簡(jiǎn)婷 安科瑞電氣股份有限公司 上海嘉定 ?201801 技術(shù)支持18701998775 ? 治理中線電能質(zhì)量問(wèn)題的設(shè)備通常被稱為中線安防保護(hù)器或中線電流消除器。這類設(shè)備主要用于解決三相四線制供電系統(tǒng)
    的頭像 發(fā)表于 10-24 16:05 ?857次閱讀
    為什么需要<b class='flag-5'>中線</b>安防保護(hù)器?

    常用電子元器件使用手冊(cè)

    電子發(fā)燒友網(wǎng)站提供《常用電子元器件使用手冊(cè).pdf》資料免費(fèi)下載
    發(fā)表于 09-25 16:07 ?77次下載

    聚焦中線防護(hù)痛點(diǎn),安科瑞中線安防保護(hù)器賦能多場(chǎng)景用電安全

    在電氣安全防護(hù)體系中,中線(中性線)作為電力系統(tǒng)的關(guān)鍵組成部分,其穩(wěn)定性直接關(guān)系到用電設(shè)備的安全運(yùn)行與人員觸電風(fēng)險(xiǎn)防控。安科瑞中線安防保護(hù)器作為針對(duì)中線故障的專項(xiàng)防護(hù)設(shè)備,能有效解決中線
    的頭像 發(fā)表于 09-19 16:50 ?887次閱讀
    聚焦<b class='flag-5'>中線</b>防護(hù)痛點(diǎn),安科瑞<b class='flag-5'>中線</b>安防保護(hù)器賦能多場(chǎng)景用電安全

    【HZ-T536開發(fā)板免費(fèi)體驗(yàn)】—— linux創(chuàng)建線程

    的執(zhí)行任務(wù)成為單線程。多線程是程序中包含多個(gè)執(zhí)行流,在一個(gè)程序中可以同時(shí)運(yùn)行多個(gè)不同的線程來(lái)執(zhí)行不同的任務(wù)。 多線程提高了CPU的使用鹵率。多線程
    發(fā)表于 09-01 21:31

    緩存之美:從根上理解 ConcurrentHashMap

    表的占用,以保持并發(fā)可讀性,次要目的是保持空間消耗與 HashMap 相同或更好,并支持利用多線程在空表上高效地插入初始值。在 Java 8 及之后的版本,使用 CAS 操作
    的頭像 發(fā)表于 08-05 14:48 ?671次閱讀

    同步任務(wù)開發(fā)指導(dǎo)

    同步任務(wù)是指在多個(gè)線程之間協(xié)調(diào)執(zhí)行的任務(wù),其目的是確保多個(gè)任務(wù)按照一定的順序和規(guī)則執(zhí)行,例如使用鎖來(lái)防止數(shù)據(jù)競(jìng)爭(zhēng)。 同步任務(wù)的實(shí)現(xiàn)需要考慮多個(gè)線程之間的協(xié)作和
    發(fā)表于 06-19 07:57

    鴻蒙5開發(fā)寶藏案例分享---應(yīng)用并發(fā)設(shè)計(jì)

    ?** 鴻蒙并發(fā)編程實(shí)戰(zhàn)指南:解鎖ArkTS多線程黑科技** 嘿,開發(fā)者朋友們! 今天給大家扒一扒鴻蒙官方文檔里藏著的并發(fā)編程寶藏—— 10
    發(fā)表于 06-12 16:19
    渭南市| 曲阳县| 道孚县| 镇远县| 海门市| 中宁县| 砀山县| 五大连池市| 广宁县| 靖边县| 平乐县| 横峰县| 东至县| 惠水县| 方城县| 丹巴县| 饶河县| 天柱县| 勐海县| 哈尔滨市| 长垣县| 宝丰县| 阳西县| 龙里县| 临沭县| 连云港市| 通河县| 广州市| 鄂托克前旗| 武平县| 宝应县| 玛曲县| 江源县| 丹江口市| 凌源市| 五寨县| 崇文区| 通渭县| 黔西| 昭苏县| 罗甸县|