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

聊聊Java并發(fā)中的Synchronized

 更新時(shí)間:2017年11月16日 15:34:51   投稿:mengwei  
這篇文章主要介紹了聊聊Java并發(fā)中的Synchronized,介紹了同步的基礎(chǔ),原理,鎖的相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。

1 引言

在多線程并發(fā)編程中Synchronized一直是元老級(jí)角色,很多人都會(huì)稱呼它為重量級(jí)鎖,但是隨著Java SE1.6對(duì)Synchronized進(jìn)行了各種優(yōu)化之后,有些情況下它并不那么重了,本文詳細(xì)介紹了Java SE1.6中為了減少獲得鎖和釋放鎖帶來(lái)的性能消耗而引入的偏向鎖和輕量級(jí)鎖,以及鎖的存儲(chǔ)結(jié)構(gòu)和升級(jí)過(guò)程。

2 術(shù)語(yǔ)定義

術(shù)語(yǔ) 英文 說(shuō)明
CAS Compare and Swap 比較并設(shè)置。用于在硬件層面上提供原子性操作。在 Intel 處理器中,比較并交換通過(guò)指令cmpxchg實(shí)現(xiàn)。比較是否和給定的數(shù)值一致,如果一致則修改,不一致則不修改

3同步的基礎(chǔ)

Java中的每一個(gè)對(duì)象都可以作為鎖。

對(duì)于同步方法,鎖是當(dāng)前實(shí)例對(duì)象。

對(duì)于靜態(tài)同步方法,鎖是當(dāng)前對(duì)象的Class對(duì)象。

對(duì)于同步方法塊,鎖是Synchonized括號(hào)里配置的對(duì)象。

當(dāng)一個(gè)線程試圖訪問(wèn)同步代碼塊時(shí),它首先必須得到鎖,退出或拋出異常時(shí)必須釋放鎖。那么鎖存在哪里呢?鎖里面會(huì)存儲(chǔ)什么信息呢?

4同步的原理

JVM規(guī)范規(guī)定JVM基于進(jìn)入和退出Monitor對(duì)象來(lái)實(shí)現(xiàn)方法同步和代碼塊同步,但兩者的實(shí)現(xiàn)細(xì)節(jié)不一樣。代碼塊同步是使用monitorenter和monitorexit指令實(shí)現(xiàn),而方法同步是使用另外一種方式實(shí)現(xiàn)的,細(xì)節(jié)在JVM規(guī)范里并沒(méi)有詳細(xì)說(shuō)明,但是方法的同步同樣可以使用這兩個(gè)指令來(lái)實(shí)現(xiàn)。monitorenter指令是在編譯后插入到同步代碼塊的開(kāi)始位置,而monitorexit是插入到方法結(jié)束處和異常處,JVM要保證每個(gè)monitorenter必須有對(duì)應(yīng)的monitorexit與之配對(duì)。任何對(duì)象都有一個(gè)monitor與之關(guān)聯(lián),當(dāng)且一個(gè)monitor被持有后,它將處于鎖定狀態(tài)。線程執(zhí)行到monitorenter指令時(shí),將會(huì)嘗試獲取對(duì)象所對(duì)應(yīng)的monitor的所有權(quán),即嘗試獲得對(duì)象的鎖。

4.1Java對(duì)象頭

鎖存在Java對(duì)象頭里。如果對(duì)象是數(shù)組類型,則虛擬機(jī)用3個(gè)Word(字寬)存儲(chǔ)對(duì)象頭,如果對(duì)象是非數(shù)組類型,則用2字寬存儲(chǔ)對(duì)象頭。在32位虛擬機(jī)中,一字寬等于四字節(jié),即32bit。

長(zhǎng)度 內(nèi)容 說(shuō)明
32/64bit Mark Word 存儲(chǔ)對(duì)象的hashCode或鎖信息等。
32/64bit Class Metadata Address 存儲(chǔ)到對(duì)象類型數(shù)據(jù)的指針
32/64bit Array length 數(shù)組的長(zhǎng)度(如果當(dāng)前對(duì)象是數(shù)組)

Java對(duì)象頭里的Mark Word里默認(rèn)存儲(chǔ)對(duì)象的HashCode,分代年齡和鎖標(biāo)記位。32位JVM的Mark Word的默認(rèn)存儲(chǔ)結(jié)構(gòu)如下:


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

在運(yùn)行期間Mark Word里存儲(chǔ)的數(shù)據(jù)會(huì)隨著鎖標(biāo)志位的變化而變化。Mark Word可能變化為存儲(chǔ)以下4種數(shù)據(jù):

鎖狀態(tài)

25 bit

4bit

1bit 2bit
23bit 2bit 是否是偏向鎖 鎖標(biāo)志位
輕量級(jí)鎖 指向棧中鎖記錄的指針 00
重量級(jí)鎖 指向互斥量(重量級(jí)鎖)的指針 10
GC標(biāo)記 11
偏向鎖 線程ID Epoch 對(duì)象分代年齡 1 01

在64位虛擬機(jī)下,Mark Word是64bit大小的,其存儲(chǔ)結(jié)構(gòu)如下:

鎖狀態(tài)

25bit

31bit

1bit

4bit

1bit 2bit
cms_free 分代年齡 偏向鎖 鎖標(biāo)志位
無(wú)鎖 unused hashCode 0 01
偏向鎖 ThreadID(54bit) Epoch(2bit) 1 01

4.2鎖的升級(jí)

JavaSE1.6為了減少獲得鎖和釋放鎖所帶來(lái)的性能消耗,引入了“偏向鎖”和“輕量級(jí)鎖”,所以在JavaSE1.6里鎖一共有四種狀態(tài),無(wú)鎖狀態(tài),偏向鎖狀態(tài),輕量級(jí)鎖狀態(tài)和重量級(jí)鎖狀態(tài),它會(huì)隨著競(jìng)爭(zhēng)情況逐漸升級(jí)。鎖可以升級(jí)但不能降級(jí),意味著偏向鎖升級(jí)成輕量級(jí)鎖后不能降級(jí)成偏向鎖。這種鎖升級(jí)卻不能降級(jí)的策略,目的是為了提高獲得鎖和釋放鎖的效率,下文會(huì)詳細(xì)分析。

4.3偏向鎖

Hotspot的作者經(jīng)過(guò)以往的研究發(fā)現(xiàn)大多數(shù)情況下鎖不僅不存在多線程競(jìng)爭(zhēng),而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價(jià)更低而引入了偏向鎖。當(dāng)一個(gè)線程訪問(wèn)同步塊并獲取鎖時(shí),會(huì)在對(duì)象頭和棧幀中的鎖記錄里存儲(chǔ)鎖偏向的線程ID,以后該線程在進(jìn)入和退出同步塊時(shí)不需要花費(fèi)CAS操作來(lái)加鎖和解鎖,而只需簡(jiǎn)單的測(cè)試一下對(duì)象頭的MarkWord里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖,如果測(cè)試成功,表示線程已經(jīng)獲得了鎖,如果測(cè)試失敗,則需要再測(cè)試下MarkWord中偏向鎖的標(biāo)識(shí)是否設(shè)置成1(表示當(dāng)前是偏向鎖),如果沒(méi)有設(shè)置,則使用CAS競(jìng)爭(zhēng)鎖,如果設(shè)置了,則嘗試使用CAS將對(duì)象頭的偏向鎖指向當(dāng)前線程。

偏向鎖的撤銷:偏向鎖使用了一種等到競(jìng)爭(zhēng)出現(xiàn)才釋放鎖的機(jī)制,所以當(dāng)其他線程嘗試競(jìng)爭(zhēng)偏向鎖時(shí),持有偏向鎖的線程才會(huì)釋放鎖。偏向鎖的撤銷,需要等待全局安全點(diǎn)(在這個(gè)時(shí)間點(diǎn)上沒(méi)有字節(jié)碼正在執(zhí)行),它會(huì)首先暫停擁有偏向鎖的線程,然后檢查持有偏向鎖的線程是否活著,如果線程不處于活動(dòng)狀態(tài),則將對(duì)象頭設(shè)置成無(wú)鎖狀態(tài),如果線程仍然活著,擁有偏向鎖的棧會(huì)被執(zhí)行,遍歷偏向?qū)ο蟮逆i記錄,棧中的鎖記錄和對(duì)象頭的MarkWord要么重新偏向于其他線程,要么恢復(fù)到無(wú)鎖或者標(biāo)記對(duì)象不適合作為偏向鎖,最后喚醒暫停的線程。下圖中的線程1演示了偏向鎖初始化的流程,線程2演示了偏向鎖撤銷的流程。

關(guān)閉偏向鎖:偏向鎖在Java 6和Java 7里是默認(rèn)啟用的,但是它在應(yīng)用程序啟動(dòng)幾秒鐘之后才激活,如有必要可以使用JVM參數(shù)來(lái)關(guān)閉延遲-XX:BiasedLockingStartupDelay = 0。如果你確定自己應(yīng)用程序里所有的鎖通常情況下處于競(jìng)爭(zhēng)狀態(tài),可以通過(guò)JVM參數(shù)關(guān)閉偏向鎖-XX:-UseBiasedLocking=false,那么默認(rèn)會(huì)進(jìn)入輕量級(jí)鎖狀態(tài)。

4.4 輕量級(jí)鎖

輕量級(jí)鎖加鎖:線程在執(zhí)行同步塊之前,JVM會(huì)先在當(dāng)前線程的棧楨中創(chuàng)建用于存儲(chǔ)鎖記錄的空間,并將對(duì)象頭中的Mark Word復(fù)制到鎖記錄中,官方稱為Displaced Mark Word。然后線程嘗試使用CAS將對(duì)象頭中的Mark Word替換為指向鎖記錄的指針。如果成功,當(dāng)前線程獲得鎖,如果失敗,表示其他線程競(jìng)爭(zhēng)鎖,當(dāng)前線程便嘗試使用自旋來(lái)獲取鎖。

輕量級(jí)鎖解鎖:輕量級(jí)解鎖時(shí),會(huì)使用原子的CAS操作來(lái)將Displaced Mark Word替換回到對(duì)象頭,如果成功,則表示沒(méi)有競(jìng)爭(zhēng)發(fā)生。如果失敗,表示當(dāng)前鎖存在競(jìng)爭(zhēng),鎖就會(huì)膨脹成重量級(jí)鎖。下圖是兩個(gè)線程同時(shí)爭(zhēng)奪鎖,導(dǎo)致鎖膨脹的流程圖。

因?yàn)樽孕龝?huì)消耗CPU,為了避免無(wú)用的自旋(比如獲得鎖的線程被阻塞住了),一旦鎖升級(jí)成重量級(jí)鎖,就不會(huì)再恢復(fù)到輕量級(jí)鎖狀態(tài)。當(dāng)鎖處于這個(gè)狀態(tài)下,其他線程試圖獲取鎖時(shí),都會(huì)被阻塞住,當(dāng)持有鎖的線程釋放鎖之后會(huì)喚醒這些線程,被喚醒的線程就會(huì)進(jìn)行新一輪的奪鎖之爭(zhēng)。

5 鎖的優(yōu)缺點(diǎn)對(duì)比

優(yōu)點(diǎn)

缺點(diǎn)

適用場(chǎng)景

偏向鎖

加鎖和解鎖不需要額外的消耗,和執(zhí)行非同步方法比僅存在納秒級(jí)的差距。

如果線程間存在鎖競(jìng)爭(zhēng),會(huì)帶來(lái)額外的鎖撤銷的消耗。

適用于只有一個(gè)線程訪問(wèn)同步塊場(chǎng)景。

輕量級(jí)鎖

競(jìng)爭(zhēng)的線程不會(huì)阻塞,提高了程序的響應(yīng)速度。

如果始終得不到鎖競(jìng)爭(zhēng)的線程使用自旋會(huì)消耗CPU。

追求響應(yīng)時(shí)間。

同步塊執(zhí)行速度非???。

重量級(jí)鎖

線程競(jìng)爭(zhēng)不使用自旋,不會(huì)消耗CPU。

線程阻塞,響應(yīng)時(shí)間緩慢。

追求吞吐量。

同步塊執(zhí)行速度較長(zhǎng)。

6 參考源碼

本文一些內(nèi)容參考了HotSpot源碼 。對(duì)象頭源碼markOop.hpp。偏向鎖源碼biasedLocking.cpp。以及其他源碼ObjectMonitor.cpp和BasicLock.cpp。

總結(jié)

以上就是本文關(guān)于聊聊Java并發(fā)中的Synchronized的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:

Javaweb應(yīng)用使用限流處理大量的并發(fā)請(qǐng)求詳解

java并發(fā)學(xué)習(xí)之BlockingQueue實(shí)現(xiàn)生產(chǎn)者消費(fèi)者詳解

Java系統(tǒng)的高并發(fā)解決方法詳解

如有不足之處,歡迎留言指出。

相關(guān)文章

  • Java三種求水仙花數(shù)的方法

    Java三種求水仙花數(shù)的方法

    本篇文章通過(guò)求水仙花數(shù)的實(shí)例來(lái)讓大家對(duì)JAVA求數(shù)的概念和方法有更深入的理解和應(yīng)用,學(xué)習(xí)參考下吧。
    2018-02-02
  • 詳解Java語(yǔ)言中一個(gè)字符占幾個(gè)字節(jié)?

    詳解Java語(yǔ)言中一個(gè)字符占幾個(gè)字節(jié)?

    這篇文章主要介紹了Java語(yǔ)言中一個(gè)字符占幾個(gè)字節(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • Java中的cglib原理解析

    Java中的cglib原理解析

    這篇文章主要介紹了Java中的cglib原理解析,由于代理類繼承了被代理類,所以調(diào)用sayHello()方法時(shí)會(huì)直接調(diào)用代理類的sayHello()方法,而在代理類的方法中,調(diào)用了Callback的邏輯,需要的朋友可以參考下
    2023-10-10
  • Java 基礎(chǔ)--Arrays工具類詳解

    Java 基礎(chǔ)--Arrays工具類詳解

    這篇文章主要介紹了Java Arrays工具類用法,結(jié)合實(shí)例形式分析了java Arrays工具類針對(duì)數(shù)組元素修改、復(fù)制、排序等操作使用技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2021-09-09
  • SpringMvc后臺(tái)接收json數(shù)據(jù)中文亂碼問(wèn)題詳解

    SpringMvc后臺(tái)接收json數(shù)據(jù)中文亂碼問(wèn)題詳解

    這篇文章主要介紹了SpringMvc后臺(tái)接收json數(shù)據(jù)中文亂碼問(wèn)題詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • java實(shí)現(xiàn)字符串排列組合問(wèn)題

    java實(shí)現(xiàn)字符串排列組合問(wèn)題

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)字符串排列組合問(wèn)題,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • java把字符串轉(zhuǎn)化成公式計(jì)算的示例

    java把字符串轉(zhuǎn)化成公式計(jì)算的示例

    今天小編就為大家分享一篇java把字符串轉(zhuǎn)化成公式計(jì)算的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • Spring?Security認(rèn)證器實(shí)現(xiàn)過(guò)程詳解

    Spring?Security認(rèn)證器實(shí)現(xiàn)過(guò)程詳解

    一些權(quán)限框架一般都包含認(rèn)證器和決策器,前者處理登陸驗(yàn)證,后者處理訪問(wèn)資源的控制,這篇文章主要介紹了Spring?Security認(rèn)證器實(shí)現(xiàn)過(guò)程,需要的朋友可以參考下
    2022-06-06
  • Java工具類實(shí)現(xiàn)高效編寫報(bào)表

    Java工具類實(shí)現(xiàn)高效編寫報(bào)表

    對(duì)于報(bào)表數(shù)據(jù)大部分情況下使用寫sql的方式為大屏/報(bào)表提供數(shù)據(jù)來(lái)源,但是對(duì)于某些復(fù)雜情況下僅僅使用sql無(wú)法實(shí)現(xiàn),這篇文章主要介紹了Java工具類實(shí)現(xiàn)高效編寫報(bào)表
    2022-11-11
  • Spring中存對(duì)象和取對(duì)象的方式詳解

    Spring中存對(duì)象和取對(duì)象的方式詳解

    這篇文章主要介紹了Spring中存對(duì)象和取對(duì)象的方式,Spring中更簡(jiǎn)單的存對(duì)象與取對(duì)象的方式是注解,注解實(shí)現(xiàn)有兩種方式:一在編譯的時(shí)候,把注解替換成相關(guān)代碼,并添加到我們?cè)瓉?lái)的代碼中,二攔截方法,文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考下
    2024-08-08

最新評(píng)論

军事| 漳浦县| 黎城县| 泌阳县| 敦煌市| 丹寨县| 华安县| 布拖县| 乐亭县| 黄平县| 临江市| 沂水县| 东阿县| 雷山县| 汉阴县| 铁岭市| 宁都县| 襄城县| 新蔡县| 钟祥市| 新干县| 蕉岭县| 天门市| 金门县| 萝北县| 荆州市| 汤阴县| 呼伦贝尔市| 阿瓦提县| 青浦区| 锡林郭勒盟| 梁平县| 江北区| 吉首市| 怀仁县| 德钦县| 乳山市| 泗洪县| 遵义市| 泰和县| 吉木萨尔县|