国产精品天干天干,亚洲毛片在线,日韩gay小鲜肉啪啪18禁,女同Gay自慰喷水

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

ReentrantLock Lock和unlock源碼分析

2022-01-20 21:08 作者:房頂上的鋁皮水塔  | 我要投稿

參考內(nèi)容:

1. ReentrantLock 源碼分析?https://www.bilibili.com/video/BV1D54y1H7Bh?p=6

本篇內(nèi)容將分成以下幾個(gè)小節(jié):

  1. ReentrantLock中State的作用

  2. ReentrantLock中的公平鎖加鎖機(jī)制

  3. ReentrantLock中的公平鎖解鎖機(jī)制

ReentrantLock中State的作用

在并發(fā)操作的過(guò)程中,我們需要通過(guò)鎖機(jī)制來(lái)規(guī)定線程訪問(wèn)資源的順序,從而避免潛在的并發(fā)操作問(wèn)題。如果讓我們自己使用CAS自旋鎖實(shí)現(xiàn)一把鎖,我們或許可以這樣實(shí)現(xiàn):


在上面的代碼中,我們通過(guò)volatile 的state變量作為共享變量,當(dāng)state等于0時(shí),當(dāng)前的鎖沒(méi)有被線程持有。因?yàn)槲覀冃枰ㄟ^(guò)CAS操作修改state變量,所以我們首先通過(guò)反射的方式獲取到Unsafe類的實(shí)例。

如果一個(gè)線程持有了鎖,后面的線程只能不斷嘗試while循環(huán)中的CAS操作,無(wú)法執(zhí)行l(wèi)ock語(yǔ)句之后的操作,通過(guò)while來(lái)阻塞自身的代碼向后執(zhí)行。所以,我們基本實(shí)現(xiàn)了一個(gè)自旋鎖。

測(cè)試我們寫(xiě)的簡(jiǎn)單鎖

我們構(gòu)建兩個(gè)線程并發(fā)執(zhí)行,通過(guò)測(cè)試我們可以看到在一個(gè)線程調(diào)用了lock之后,兩個(gè)線程中的內(nèi)容不會(huì)交錯(cuò)執(zhí)行。

以上的代碼也驗(yàn)證了我們實(shí)現(xiàn)的Lock的正確性。

ReentrantLock中的Lock操作

如果對(duì)于我們實(shí)現(xiàn)的簡(jiǎn)易lock進(jìn)行分析,它存在兩個(gè)致命的問(wèn)題:

  1. 這個(gè)簡(jiǎn)易Lock不是可重入的

  2. 如果存在多個(gè)線程,無(wú)法保證線程的公平性

實(shí)現(xiàn)可重入鎖比較簡(jiǎn)單,在lock的時(shí)候判斷想要加鎖的線程是否是當(dāng)前線程即可:

其實(shí)這里有點(diǎn)問(wèn)題,因?yàn)閏ur != Thread.currentThread()并不是原子操作,其實(shí)改成原子操作比較好。

但是公平性無(wú)法保證,如果有三個(gè)線程同時(shí)競(jìng)爭(zhēng),可能會(huì)出現(xiàn)如下的情況:

我想要Thread2先于Thread3執(zhí)行,因?yàn)門hread2和Thread3都在自旋,如果Thread3先CAS操作成功, 則會(huì)先于Thread2執(zhí)行。

對(duì)于以上兩個(gè)問(wèn)題,在JDK中的ReentrantLock類都有解決。

ReentrantLock的結(jié)構(gòu)

ReentrantLock中的內(nèi)部類Sync繼承自AbstractQueuedSynchronizer。AQS在其內(nèi)部實(shí)現(xiàn)了一個(gè)隊(duì)列,并且通過(guò)其內(nèi)部屬性state的值說(shuō)明當(dāng)前鎖被獲取(acquire)和釋放(release)的意義。因?yàn)镽eentrantLock是可重入鎖,當(dāng)state=0時(shí),表示當(dāng)前鎖沒(méi)有被持有,當(dāng)state>=1說(shuō)明當(dāng)前鎖被持有,state表示鎖的重入的次數(shù)。具體如下圖所示

ReentrantLock中一個(gè)表示節(jié)點(diǎn)的內(nèi)部類是Node:

所以,ReentrantLock大體上是通過(guò)線程排隊(duì)的機(jī)制來(lái)控制線程訪問(wèn)資源的【公平性】,具體的操作下面的講到。

ReentrantLock#lock

如果不指定ReentrantLock構(gòu)造函數(shù)的參數(shù),默認(rèn)情況下是非公平鎖。

公平鎖的加鎖方式

公平鎖的加鎖過(guò)程的調(diào)用鏈如圖所示:

我們首先看看公平鎖:

當(dāng)ReentrantLock調(diào)用lock方法時(shí),會(huì)根據(jù)內(nèi)部Sync的實(shí)現(xiàn)類調(diào)用具體的lock方法。

在后續(xù)的acquire中,首先會(huì)判斷當(dāng)前線程能否獲取鎖(tryAcquire),如果可以獲取鎖,就嘗試構(gòu)建當(dāng)前以線程為參數(shù)的Node節(jié)點(diǎn)(addWaiter),并且將構(gòu)建好的、在隊(duì)列中已經(jīng)排隊(duì)了的節(jié)點(diǎn)進(jìn)行一些操作(acquireQueued)。通常如果一個(gè)線程正常的獲取到鎖,tryAcquire返回false,acquireQueued返回false。

下面我們來(lái)看看具體的代碼邏輯。

ReentrantLock#tryAcquire

tryAcquire在公平鎖的實(shí)現(xiàn)中,會(huì)首先獲取state,如果當(dāng)前鎖沒(méi)有被占用,就會(huì)檢查隊(duì)列中是否有在排隊(duì)的線程。之所以要檢查,是因?yàn)闉榱藵M足公平鎖排隊(duì)的需要。

如果當(dāng)前的線程之前沒(méi)有排隊(duì)的線程,而且CAS修改state成功,設(shè)置鎖當(dāng)前持有的線程為自己(setExclusiveOwnerThread(current);)?

如果以上操作失敗,說(shuō)明當(dāng)前鎖被其他線程占有,如果當(dāng)前獲取鎖的線程是自身,則可以重入,增加state,表示重入次數(shù)。

其代碼邏輯總結(jié)為下圖:

如果當(dāng)前鎖被持有,同時(shí)也不是能重入的情況,返回false,說(shuō)明當(dāng)前的線程需要被阻塞。我們就需要去addWaiter看看

ReentrantLock#addWaiter

ReentrantLock中的隊(duì)列是雙向鏈表,新加入隊(duì)列的線程首先會(huì)考慮能否放到當(dāng)前線程隊(duì)列的最后,如果不可以放入則會(huì)考慮調(diào)用enq

enq使用了自旋鎖確保當(dāng)前的節(jié)點(diǎn)作為隊(duì)列的最后一個(gè)元素被添加,同時(shí)返回這個(gè)節(jié)點(diǎn)。

在enq方法中,首先會(huì)檢查當(dāng)前尾結(jié)點(diǎn)是否為空,如果為空,同時(shí)創(chuàng)建頭結(jié)點(diǎn),然后進(jìn)入for循環(huán)的第二輪,使用CAS機(jī)制,將node設(shè)置為tail。

通過(guò)addWaiter,可以保證并發(fā)嘗試獲取鎖的線程獲取鎖失敗時(shí),按照順序被加入隊(duì)列。

ReentrantLock#acquireQueued

acquireQueued方法主要是進(jìn)行判斷當(dāng)前的線程是否需要阻塞的邏輯。如果當(dāng)前線程的前一個(gè)節(jié)點(diǎn)(node.predecessor)是頭結(jié)點(diǎn),而且獲取鎖成功(tryAcquire),則當(dāng)前的線程不需要被阻塞,并且調(diào)整當(dāng)前節(jié)點(diǎn)為頭結(jié)點(diǎn);否則當(dāng)前的線程需要在隊(duì)列中阻塞等待(調(diào)用park相關(guān)的方法,并且修改waitStatus)。


修改waitStatus和park的操作是在shouldParkAfterFailedAcquire中進(jìn)行的。waitStatus是一個(gè)非常重要的屬性,在構(gòu)建Node的時(shí)候默認(rèn)初始化的值為0。當(dāng)后面有一個(gè)線程排隊(duì)在它后面時(shí),后面的節(jié)點(diǎn)會(huì)通過(guò)prev屬性找到前面的節(jié)點(diǎn),并且修改waitStatus為-1(SIGNAL)。這意味著當(dāng)一個(gè)線程釋放鎖的時(shí)候,檢查自己的waitStatus,如果為SIGNAL,就需要unpark后面的線程。

因?yàn)槌跏嫉念^結(jié)點(diǎn)的waitStatus為0,if條件會(huì)走到最后,通過(guò)CAS操作修改上一個(gè)節(jié)點(diǎn)的waitStatus為SIGNAL,并且返回false。在acquireQueued方法中,for循環(huán)也會(huì)執(zhí)行第二次,此時(shí)shouldParkAfterFailedAcquire獲取上一個(gè)節(jié)點(diǎn)的waitStatus為SIGNAL,并且返回true。

在parkAndCheckInterrupt中調(diào)用lockSupport阻塞。這里有一個(gè)小細(xì)節(jié),LockSupport.lock中傳入的參數(shù)是AQS,在ReentrantLock中的也就是FairSync或者是NonfairSync。sync對(duì)象是在ReentrantLock對(duì)象的實(shí)例中的,在各個(gè)并發(fā)的線程之間是共享的,也就是說(shuō)并發(fā)等待的線程是在等待sync對(duì)象的解鎖。

acquireQueued的過(guò)程總結(jié)成圖就是這樣的形式:

總結(jié)線程的排隊(duì)和加鎖過(guò)程

這里通過(guò)畫(huà)圖的形式表現(xiàn)序號(hào)為1 2 3 的三個(gè)線程進(jìn)入ReentrantLock時(shí)的排隊(duì)情況。這三個(gè)線程滿足如下的假設(shè):

線程1首先獲取到鎖,并且在線程2進(jìn)入隊(duì)列之后釋放。線程2直到3進(jìn)入隊(duì)列之后才會(huì)釋放鎖?;谝陨霞僭O(shè)我們看看具體的圖示:

#1 線程1直接獲取到鎖(tryAcquire返回true),線程1不會(huì)阻塞正常執(zhí)行。ReentrantLock的sync隊(duì)列中的head和tail指針指向的都是null元素

#2 線程2進(jìn)入ReentrantLock lock邏輯,tryAcquire失敗,主要有兩個(gè)原因,第一當(dāng)前的state!=0,并且因?yàn)槌钟墟i的線程是線程1,不可以重入。

繼續(xù)走enq邏輯,在第一次循環(huán)構(gòu)建一個(gè)空頭結(jié)點(diǎn),在第二次循環(huán)時(shí)將線程2放入這個(gè)空頭節(jié)點(diǎn)之后。

構(gòu)建空頭結(jié)點(diǎn)的意義我想不需要過(guò)多說(shuō)明,主要是在數(shù)據(jù)結(jié)構(gòu)中使得操作更加簡(jiǎn)單。

在acqurieQueued是因?yàn)楫?dāng)前的線程2的上一個(gè)節(jié)點(diǎn)時(shí)頭結(jié)點(diǎn),假設(shè)此時(shí)線程1釋放鎖,線程2調(diào)用tryAcquire成功,則當(dāng)前的頭結(jié)點(diǎn)改成線程2。

#3 線程3進(jìn)入lock邏輯,tryAcquire失敗,進(jìn)入addWaiter中,并且將當(dāng)前的線程排隊(duì)在線程2之后

但是當(dāng)前線程的進(jìn)入acquireQueued邏輯時(shí),tryAcquire返回false,所以線程3修改線程2的waitStatus,并且被park,阻塞。

三個(gè)線程的加鎖過(guò)程大概就是這樣子。


非公平鎖的加鎖過(guò)程

對(duì)于非公平鎖而言,假設(shè)有多個(gè)線程并發(fā)訪問(wèn), 他們都會(huì)立刻訪問(wèn)并且嘗試修改State,如果state修改失敗,就會(huì)調(diào)用acquire

非公平鎖和公平鎖之間差別也不大,主要是公平鎖會(huì)詢問(wèn)隊(duì)列中是否存在排隊(duì)的線程,但是非公平鎖中沒(méi)有。

公平鎖和非公平鎖的解鎖過(guò)程

解鎖的過(guò)程對(duì)于公平和非公平鎖沒(méi)有具體的區(qū)分,主要的調(diào)用鏈如下圖:

在unparkSusccessor中就會(huì)對(duì)后面被阻塞的線程進(jìn)行unpark:


總結(jié)時(shí)間:

  1. ReentrantLock內(nèi)部有一個(gè)非常重要的屬性:state 通過(guò)讀取state可以知道當(dāng)前的鎖有無(wú)線程占用

  2. ReentrantLock內(nèi)部Sync的實(shí)現(xiàn)分成公平和非公平。公平鎖是指線程在沒(méi)有獲取到鎖時(shí)需要排隊(duì),按照先后順序排隊(duì)。后排隊(duì)的線程會(huì)修改前面的waitStatus,以便在釋放鎖的時(shí)候確定當(dāng)前的線程后續(xù)是否有線程需要被unpark(解阻塞)

  3. 非公平鎖相對(duì)于公平鎖而言在剛剛獲取鎖的時(shí)候線程會(huì)競(jìng)爭(zhēng)CAS修改state,成功者獲取到鎖,失敗者就還是會(huì)像公平鎖一樣。但是相對(duì)于公平鎖,非公平鎖不會(huì)查看隊(duì)列中前面是否有排隊(duì)的線程。

  4. 解鎖的過(guò)程依賴的是LockSupport的unpark。對(duì)于公平和非公平鎖都差不多,沒(méi)有什么區(qū)別。


ReentrantLock Lock和unlock源碼分析的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
邵东县| 土默特右旗| 正阳县| 正镶白旗| 娄底市| 庆元县| 凤冈县| 横山县| 临夏县| 辉县市| 阳西县| 长宁区| 武汉市| 建德市| 淄博市| 梓潼县| 栾城县| 泊头市| 义乌市| 桂东县| 邮箱| 东安县| 孟州市| 额尔古纳市| 皋兰县| 奎屯市| 台中县| 马关县| 安化县| 阿图什市| 老河口市| 扬中市| 霍林郭勒市| 北辰区| 古浪县| 开平市| 罗田县| 新安县| 青阳县| 治县。| 赞皇县|