<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JAVA鎖優(yōu)化和膨脹過程

          共 3053字,需瀏覽 7分鐘

           ·

          2021-05-28 04:04

          HotSpot虛擬機(jī)開發(fā)團(tuán)隊(duì)在這個(gè)版本上花費(fèi)了大量的精力去實(shí)現(xiàn)各種鎖優(yōu)化技術(shù),如適應(yīng)性自旋(Adaptive Spinning)、鎖削除(Lock Elimination)、鎖膨脹(Lock Coarsening)、輕量級(jí)鎖(Lightweight Locking)、偏向鎖(Biased Locking)等,這些技術(shù)都是為了在線程之間更高效地共享數(shù)據(jù),以及解決競爭問題,從而提高程序的執(zhí)行效率。

          自旋鎖

          自選鎖其實(shí)就是在拿鎖時(shí)發(fā)現(xiàn)已經(jīng)有線程拿了鎖,自己如果去拿會(huì)阻塞自己,這個(gè)時(shí)候會(huì)選擇進(jìn)行一次循環(huán)嘗試。也就是不停循環(huán)看是否能等到上個(gè)線程自己釋放鎖。這個(gè)問題是基于一個(gè)現(xiàn)實(shí)考量的:很多拿了鎖的線程會(huì)很快釋放鎖。因?yàn)橐话忝舾械牟僮鞑粫?huì)很多。當(dāng)然這個(gè)是一個(gè)不能完全確定的情況,只能說總體上是一種優(yōu)化。

          舉個(gè)例子就好比一個(gè)人要上廁所發(fā)現(xiàn)廁所里面有人,他可以:

          1,等一小會(huì)。
          2,跑去另外的地方上廁所。

          等一小會(huì)不一定能等到前一個(gè)人出來,不過如果跑去別的廁所的花費(fèi)的時(shí)間肯定比等一小會(huì)結(jié)果前一個(gè)人出來了長。
          當(dāng)然等完了結(jié)果那個(gè)人沒出來還是要跑去別的地方上廁所這是最慢的。

          然后是基于這種做法的一個(gè)優(yōu)化:自適應(yīng)自旋鎖
          也就是說,第一次設(shè)置最多自旋10次,結(jié)果在自旋的過程中成功獲得了鎖,那么下一次就可以設(shè)置成最多自旋20次。
          道理是:一個(gè)鎖如果能夠在自旋的過程中被釋放說明很有可能下一次也會(huì)發(fā)生這種事。
          那么就更要給這個(gè)鎖某種“便利”方便其不阻塞
          鎖(畢竟快了很多)。
          同樣如果多次嘗試的結(jié)果是完全不能自旋等到其釋放鎖,那么就說明很有可能這個(gè)臨界區(qū)里面的操作比較耗時(shí)間。就減小自旋的次數(shù),因?yàn)槠淇赡苄蕴×恕?/span>

          鎖粗化

          原則上為了提高運(yùn)行效率,鎖的范圍應(yīng)該盡量小,減少同步的代碼,但是這不是絕對(duì)的原則,試想有一個(gè)循環(huán),循環(huán)里面是一些敏感操作,有的人就在循環(huán)里面寫上了synchronized關(guān)鍵字。這樣確實(shí)沒錯(cuò)不過效率也許會(huì)很低,因?yàn)槠漕l繁地拿鎖釋放鎖。要知道鎖的取得(假如只考慮重量級(jí)MutexLock)是需要操作系統(tǒng)調(diào)用的,從用戶態(tài)進(jìn)入內(nèi)核態(tài),開銷很大。于是針對(duì)這種情況也許虛擬機(jī)發(fā)現(xiàn)了之后會(huì)適當(dāng)擴(kuò)大加鎖的范圍(所以叫鎖粗化)以避免頻繁的拿鎖釋放鎖的過程。

          比如像這樣的代碼:

          synchronized{
          做一些事情
          }
          synchronized{
          做另外一些事情
          }
          • 1

          • 2

          • 3

          • 4

          • 5

          • 6

          就會(huì)被粗化成:

          synchronized{
          做一些事情
          做另外一些事情
          }
          • 1

          • 2

          • 3

          • 4

          鎖消除

          通過逃逸分析發(fā)現(xiàn)其實(shí)根本就沒有別的線程產(chǎn)生競爭的可能(別的線程沒有臨界量的引用),或者同步塊內(nèi)進(jìn)行的是原子操作,而“自作多情”地給自己加上了鎖。有可能虛擬機(jī)會(huì)直接去掉這個(gè)鎖。

          偏向鎖

          在大多數(shù)的情況下,鎖不僅不存在多線程的競爭,而且總是由同一個(gè)線程獲得。因此為了讓線程獲得鎖的代價(jià)更低引入了偏向鎖的概念。偏向鎖的意思是如果一個(gè)線程獲得了一個(gè)偏向鎖,如果在接下來的一段時(shí)間中沒有其他線程來競爭鎖,那么持有偏向鎖的線程再次進(jìn)入或者退出同一個(gè)同步代碼塊,不需要再次進(jìn)行搶占鎖和釋放鎖的操作。偏向鎖可以通過 -XX:+UseBiasedLocking開啟或者關(guān)閉

          偏向鎖的獲取

          偏向鎖的獲取過程非常簡單,當(dāng)一個(gè)線程訪問同步塊獲取鎖時(shí),會(huì)在對(duì)象頭和棧幀中的鎖記錄里存儲(chǔ)偏向鎖的線程ID,表示哪個(gè)線程獲得了偏向鎖,結(jié)合前面分析的Mark Word來分析一下偏向鎖的獲取邏輯

          1 首先獲取目標(biāo)對(duì)象的Mark Word,根據(jù)鎖的標(biāo)識(shí)為epoch去判斷當(dāng)前是否處于可偏向的狀態(tài)

          2 如果為可偏向狀態(tài),則通過CAS操作將自己的線程ID寫入到MarkWord,如果CAS操作成功,則表示當(dāng)前線程成功獲取到偏向鎖,繼續(xù)執(zhí)行同步代碼塊

          3 如果是已偏向狀態(tài),先檢測(cè)MarkWord中存儲(chǔ)的threadID和當(dāng)前訪問的線程的threadID是否相等,如果相等,表示當(dāng)前線程已經(jīng)獲得了偏向鎖,則不需要再獲得鎖直接執(zhí)行同步代碼;如果不相等,則證明當(dāng)前鎖偏向于其他線程,需要撤銷偏向鎖。

          CAS:表示自旋鎖,由于線程的阻塞和喚醒需要CPU從用戶態(tài)轉(zhuǎn)為核心態(tài),頻繁的阻塞和喚醒對(duì)CPU來說性能開銷很大。同時(shí),很多對(duì)象鎖的鎖定狀態(tài)指會(huì)持續(xù)很短的時(shí)間,因此引入了自旋鎖,所謂自旋就是一個(gè)無意義的死循環(huán),在循環(huán)體內(nèi)不斷的重試競爭鎖。當(dāng)然,自旋的次數(shù)會(huì)有限制,超出指定的限制會(huì)升級(jí)到阻塞鎖。

          偏向鎖的撤銷

          當(dāng)其他線程嘗試競爭偏向鎖時(shí),持有偏向鎖的線程才會(huì)釋放偏向鎖,撤銷偏向鎖的過程需要等待一個(gè)全局安全點(diǎn)(所有工作線程都停止字節(jié)碼的執(zhí)行)。

          1 首先,暫停擁有偏向鎖的線程,然后檢查偏向鎖的線程是否為存活狀態(tài)
          2 如果線程已經(jīng)死了,直接把對(duì)象頭設(shè)置為無鎖狀態(tài)
          3 如果還活著,當(dāng)達(dá)到全局安全點(diǎn)時(shí)獲得偏向鎖的線程會(huì)被掛起,接著偏向鎖升級(jí)為輕量級(jí)鎖,然后喚醒被阻塞在全局安全點(diǎn)的線程繼續(xù)往下執(zhí)行同步代碼

          輕量級(jí)鎖

          前面我們知道,當(dāng)存在超過一個(gè)線程在競爭同一個(gè)同步代碼塊時(shí),會(huì)發(fā)生偏向鎖的撤銷。偏向鎖撤銷以后對(duì)象會(huì)可能會(huì)處于兩種狀態(tài)

          一種是不可偏向的無鎖狀態(tài),簡單來說就是已經(jīng)獲得偏向鎖的線程已經(jīng)退出了同步代碼塊,那么這個(gè)時(shí)候會(huì)撤銷偏向鎖,并升級(jí)為輕量級(jí)鎖

          一種是不可偏向的已鎖狀態(tài),簡單來說就是已經(jīng)獲得偏向鎖的線程正在執(zhí)行同步代碼塊,那么這個(gè)時(shí)候會(huì)升級(jí)到輕量級(jí)鎖并且被原持有鎖的線程獲得鎖
          那么升級(jí)到輕量級(jí)鎖以后的加鎖過程和解鎖過程是怎么樣的呢?

          輕量級(jí)鎖加鎖
          1 JVM會(huì)先在當(dāng)前線程的棧幀中創(chuàng)建用于存儲(chǔ)鎖記錄的空間(LockRecord)
          2 將對(duì)象頭中的Mark Word復(fù)制到鎖記錄中,稱為Displaced Mark Word.
          3 線程嘗試使用CAS將對(duì)象頭中的Mark Word替換為指向鎖記錄的指針
          4 如果替換成功,表示當(dāng)前線程獲得輕量級(jí)鎖,如果失敗,表示存在其他線程競爭鎖,那么當(dāng)前線程會(huì)嘗試使用CAS來獲取鎖, 當(dāng)自旋超過指定次數(shù)(可以自定義)時(shí)仍然無法獲得鎖,此時(shí)鎖會(huì)膨脹升級(jí)為重量級(jí)鎖

          輕量級(jí)鎖解鎖
          嘗試CAS操作將所記錄中的Mark Word替換回到對(duì)象頭中
          如果成功,表示沒有競爭發(fā)生
          如果失敗,表示當(dāng)前鎖存在競爭,鎖會(huì)膨脹成重量級(jí)鎖

          重量級(jí)鎖

          重量級(jí)鎖依賴對(duì)象內(nèi)部的monitor鎖來實(shí)現(xiàn),而monitor又依賴操作系統(tǒng)的MutexLock(互斥鎖)

          大家如果對(duì)MutexLock有興趣,可以抽時(shí)間去了解,假設(shè)Mutex變量的值為1,表示互斥鎖空閑,這個(gè)時(shí)候某個(gè)線程調(diào)用lock可以獲得鎖,而Mutex的值為0表示互斥鎖已經(jīng)被其他線程獲得,其他線程調(diào)用lock只能掛起等待

          為什么重量級(jí)鎖的開銷比較大呢?
          原因是當(dāng)系統(tǒng)檢查到是重量級(jí)鎖之后,會(huì)把等待想要獲取鎖的線程阻塞,被阻塞的線程不會(huì)消耗CPU,但是阻塞或者喚醒一個(gè)線程,都需要通過操作系統(tǒng)來實(shí)現(xiàn),也就是相當(dāng)于從用戶態(tài)轉(zhuǎn)化到內(nèi)核態(tài),而轉(zhuǎn)化狀態(tài)是需要消耗時(shí)間的

          鎖的膨脹過程

          首先簡單說下先偏向鎖、輕量級(jí)鎖、重量級(jí)鎖三者各自的應(yīng)用場景:

          偏向鎖: 只有一個(gè)線程進(jìn)入臨界區(qū);
          輕量級(jí)鎖: 多個(gè)線程交替進(jìn)入臨界區(qū);
          重量級(jí)鎖: 多個(gè)線程同時(shí)進(jìn)入臨界區(qū)。

          首先它們的關(guān)系是:最高效的是偏向鎖,盡量使用偏向鎖,如果不能(發(fā)生了競爭)就膨脹為輕量級(jí)鎖,最后是重量級(jí)鎖

          瀏覽 49
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一级黄片99日日 | 天天草天天草 | 青青草成人在线免费观看视频 | 国产内射免费视频 | 亚洲午夜剧场 |