真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

怎么剖析volatile、synchronized實(shí)現(xiàn)原理

這篇文章給大家介紹怎么剖析volatile、synchronized實(shí)現(xiàn)原理,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

沿河ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書未來(lái)市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!

前言

在java并發(fā)編程中volatile和synchronized都扮演著重要的角色。兩者都起到相同的作用:保證共享變量的線程可見性。與synchronized相比volatile可以看做是輕量級(jí)的synchronized,沒有線程的上下文切換和調(diào)試,性能比synchronized要好很多。但需要注意的是volatile變量在復(fù)合操作的時(shí)候并不能保證線程安全,相反sychronized能。下面從底層看一下volatile、synchronized到底是怎么實(shí)現(xiàn)的。

volatile

在介紹volatile前,我們先來(lái)簡(jiǎn)單介紹一下java的內(nèi)存模型

public int i = 1

假設(shè)對(duì)象有個(gè)屬性字段i,初始值為1,對(duì)象位于堆上。我們通常把堆看作是主內(nèi)存,此時(shí)分別兩個(gè)兩個(gè)不同的線程訪問字段i?,F(xiàn)代操作系統(tǒng),每個(gè)線程都分配有單獨(dú)的處理器緩存,用這些處理器緩存去緩存一些數(shù)據(jù),就可以不用再次訪問主內(nèi)存去獲取相應(yīng)的數(shù)據(jù),這樣就可以提高效率??聪聢D

這樣做可以提高效率,但同時(shí)也帶來(lái)的一個(gè)問題:修改數(shù)據(jù)時(shí),各線程的數(shù)據(jù)不一致。

這樣做可以提高效率,但同時(shí)也帶來(lái)的一個(gè)問題:修改數(shù)據(jù)時(shí),各線程的數(shù)據(jù)不一致。

所以這時(shí)候我們需要做到當(dāng)線程1修改一個(gè)共享變量時(shí),其它訪問該共享變量的線程能夠感知到變化。而這個(gè)功能volatile可以做到。我們來(lái)看下volatile到底是怎樣工作的。

volatile 只能用于修飾變量。代碼:

volatile public int i = 1;

當(dāng)volatile變量i被賦值2時(shí),這時(shí)線程1會(huì)做兩件事:

  1. 更新主內(nèi)存。

  2. 向CPU總線發(fā)送一個(gè)修改信號(hào)。

這時(shí)監(jiān)聽CPU總線的處理器會(huì)收到這個(gè)修改信號(hào)后,如果發(fā)現(xiàn)修改的數(shù)據(jù)自己緩存了,就把自己緩存的數(shù)據(jù)失效掉。這樣其它線程訪問到這段緩存時(shí)知道緩存數(shù)據(jù)失效了,需要從主內(nèi)存中獲取。這樣所有線程中的共享變量i就達(dá)到了一致性。

所以volatile也可以看作線程間通信的一種廉價(jià)方式。

synchronized

在多線程并發(fā)編程中synchronized一直扮演著元老級(jí)角色,很多人都會(huì)稱呼它為重量級(jí)鎖。但是隨著Java SE 1.6對(duì)synchronized進(jìn)行各種優(yōu)化之后,有引起情況下它就并不那重了。下面來(lái)詳細(xì)介紹這方面的內(nèi)容。

synchronized實(shí)現(xiàn)同步的基礎(chǔ)是:Java中的每個(gè)對(duì)象都可作為鎖。所以synchronized鎖的都對(duì)象,只不過(guò)不同形式下鎖的對(duì)象不一樣。

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

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

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

當(dāng)一個(gè)線程試圖訪問同步代時(shí),它必須先獲得鎖,才能執(zhí)行代碼邏輯,退出的時(shí)候又必須釋放鎖。那鎖到底是怎么實(shí)現(xiàn)的呢?

在JVM規(guī)范中規(guī)定了synchronized是通過(guò)Monitor對(duì)象來(lái)實(shí)現(xiàn)方法和代碼塊的同步,但兩者實(shí)現(xiàn)細(xì)節(jié)有點(diǎn)一不樣。代碼塊同步是使用monitorenter和monitorexit指令,方法同步是使用另外一種方法實(shí)現(xiàn),細(xì)節(jié)JVM規(guī)范并沒有詳細(xì)說(shuō)明。但是,方法的同步同樣可以使用這兩指令來(lái)實(shí)現(xiàn)。

monitorenter指令是編譯后插入到同步代碼塊的開始位置,而monitorexit指令是插入到方法結(jié)束處和異常處。JVM保證了每個(gè)monitorenter都有對(duì)應(yīng)的monitorexit。任何一個(gè)對(duì)象都有一個(gè)monitor與之關(guān)聯(lián),當(dāng)且一個(gè)monitor被持有后,對(duì)象將處于鎖定狀態(tài)。線程執(zhí)行到monitorenter指令時(shí),將會(huì)嘗試獲取對(duì)象對(duì)應(yīng)monitor的所有權(quán),即嘗試獲得對(duì)象的鎖。

那我們一直口口聲聲說(shuō)的鎖,它到底在哪里呢?遠(yuǎn)近天邊,近在眼前。上面提到對(duì)象可以作為鎖,其實(shí)鎖就在對(duì)象里面,準(zhǔn)確來(lái)說(shuō)是對(duì)象頭的Mark World結(jié)構(gòu)中。關(guān)于對(duì)象頭的結(jié)構(gòu)具體可參考:林林:聊聊java對(duì)象內(nèi)存布局

這里簡(jiǎn)單描述一下:

對(duì)象頭分為兩部分:Mark Word 與 Class Pointer(類型指針)。

Mark Word存儲(chǔ)了對(duì)象的hashCode、GC信息、鎖信息三部分,Class Pointer存儲(chǔ)了指向類對(duì)象信息的指針。在32位JVM上對(duì)象頭占用的大小是8字節(jié),64位JVM則是16字節(jié),兩種類型的Mark Word 和 Class Pointer各占一半空間大小。

下面的圖是32位JVM上面的對(duì)象頭展示。前面25bit是hashCode, 4bit是GC信息,后面兩位分別是偏向鎖標(biāo)志與鎖狀態(tài)標(biāo)志。Mark Word在不同的級(jí)別鎖時(shí),存儲(chǔ)內(nèi)容會(huì)發(fā)生變化。

上面提到Java SE 1.6對(duì)synchronized進(jìn)行各種優(yōu)化,這優(yōu)化指的是什么呢。指的是synchronized不一定就是重量級(jí)鎖,它根據(jù)鎖的重量級(jí)分成了三種,由低到高:偏向鎖、輕量級(jí)鎖、重量級(jí)鎖。上面圖就展示了每種鎖在Mark Word的存儲(chǔ)內(nèi)容。下面來(lái)介紹每種鎖的應(yīng)用場(chǎng)景和升級(jí)過(guò)程。

偏向鎖:經(jīng)過(guò)研究發(fā)現(xiàn),在多線程競(jìng)爭(zhēng)不激烈的環(huán)境或業(yè)務(wù)中,一個(gè)鎖總是由同一個(gè)線程多次獲得。這樣的話就沒有必要用生成重量級(jí)鎖和重復(fù)加鎖了,因?yàn)檫@樣代價(jià)會(huì)很高。所以這時(shí)可以通過(guò)引入偏向鎖來(lái)解決這代價(jià)過(guò)高的問題。具體做法是當(dāng)一個(gè)線程嘗試去獲取鎖時(shí),在對(duì)象頭和棧幀的鎖記錄里存儲(chǔ)指向當(dāng)前線程的偏向鎖(CAS 操作),同時(shí)設(shè)置偏向鎖標(biāo)志位為1(CAS 操作)。之后線程再對(duì)同一對(duì)象加鎖,只需要簡(jiǎn)單測(cè)試一下對(duì)象頭里面是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖就可以了,不需要真正執(zhí)行加鎖操作。這時(shí)其它線程來(lái)嘗試獲取鎖時(shí),CAS操作是獲取不了鎖的,這時(shí)只能等待原先的線程把鎖撤銷了,才能競(jìng)爭(zhēng)鎖。偏向鎖的撤銷需要等到全局安全點(diǎn)才能撤銷,這就意味著其它線程可能要等很長(zhǎng)時(shí)間。所以競(jìng)爭(zhēng)激烈的情況偏向鎖就不是很適用。這時(shí)應(yīng)該升級(jí)為輕量級(jí)鎖。

輕量級(jí)鎖:線程在執(zhí)行同步塊之前,JVM會(huì)先在當(dāng)前線程的的棧幀中創(chuàng)建用于存儲(chǔ)鎖記錄的空間,并將對(duì)象頭中的Mark World復(fù)制到線程棧幀的鎖記錄空間里面。然后呢,用CAS操作把對(duì)象頭的Mark World替換為指向鎖記錄空間的指針。成功就表示獲得鎖了,失敗就暫時(shí)自旋一下,等待其它線程解鎖。如果自旋到了時(shí)間發(fā)現(xiàn)還不能獲得鎖,這時(shí)只有兩種情況:(1)競(jìng)爭(zhēng)超激烈 (2)同步代碼執(zhí)行時(shí)間太長(zhǎng)。這時(shí)候如果還自旋是很不劃算的,因?yàn)椴坏荒芸焖佾@取鎖,還會(huì)白白浪費(fèi)了CPU。這種情景輕量級(jí)鎖就不合適了還不如升級(jí)為重量級(jí)鎖。

重量級(jí)鎖:所謂的重量級(jí)鎖,其實(shí)就是最原始和最開始java實(shí)現(xiàn)的阻塞鎖。在JVM中又叫對(duì)象監(jiān)視器。這時(shí)鎖對(duì)象的對(duì)象頭字段指向的是一個(gè)互斥量,所有線程競(jìng)爭(zhēng)重量級(jí)鎖,競(jìng)爭(zhēng)失敗的線程進(jìn)入阻塞狀態(tài)(操作系統(tǒng)層面),并且在鎖對(duì)象的一個(gè)等待池中等待被喚醒,被喚醒后的線程再次去競(jìng)爭(zhēng)鎖資源。

所以偏向鎖、輕量級(jí)鎖、重量級(jí)鎖是適用于不同的競(jìng)爭(zhēng)環(huán)境。

關(guān)于怎么剖析volatile、synchronized實(shí)現(xiàn)原理就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。


新聞標(biāo)題:怎么剖析volatile、synchronized實(shí)現(xiàn)原理
文章起源:http://weahome.cn/article/piceoh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部