軟件測(cè)試之python自動(dòng)化測(cè)試5(web/app/接口自動(dòng)化/自動(dòng)化框架)57期
了解ReentrantLock獲取和釋放鎖的過程。獲取鎖定進(jìn)程整個(gè)過程可以總結(jié)為做兩件事。成功獲取鎖,在當(dāng)前線程中執(zhí)行其他事情;鎖獲取失敗,當(dāng)前線程加入同步隊(duì)列,同時(shí)阻塞當(dāng)前線程。當(dāng)?shù)谝粋€(gè)線)進(jìn)來時(shí),通過CAS將state屬性更改為1。如果成功,通過setExclusiveOwnerThread()方法將exclusiveOwnerThread設(shè)置為當(dāng)前線程。此時(shí),當(dāng)?shù)诙€(gè)線)進(jìn)來并通過CAS將state屬性更改為1時(shí),它將失敗。這時(shí),它進(jìn)入acquire()方法。最終會(huì)得出以下方米樂M6 M6米樂法:首先通過tryAcquire()方法再次嘗試獲取鎖。tryAcquire()方法仍然通過CAS獲得鎖。此時(shí),鎖資源仍由第二個(gè)線程持有,因此它將返回false。現(xiàn)在看看acquire()方法中的if判斷。此時(shí),獲取排隊(duì)(添加服務(wù)員(節(jié)點(diǎn)。exclusiv米樂M6 M6米樂e)、arg)進(jìn)行判斷。這里需要執(zhí)行兩個(gè)方法addWaiter()和acquireQueued()。首先看addWaiter()方法。這種方法,我們需要注意以下四點(diǎn)。
首先會(huì)構(gòu)造一個(gè)node節(jié)點(diǎn)(節(jié)點(diǎn)內(nèi)部細(xì)節(jié)見其構(gòu)造方法)。如果tail(同步隊(duì)列尾節(jié)點(diǎn)指針)不為空,即同步隊(duì)列不為空,那么步驟1中構(gòu)造的節(jié)點(diǎn)將通過尾插入添加到隊(duì)列中,然后返回。如果同步隊(duì)列為空,那么執(zhí)行enq()方法,它為我們做了兩件事。如果同步隊(duì)列為空,則初始化隊(duì)列。初始化隊(duì)列后,將節(jié)點(diǎn)node入隊(duì)。通過addWaiter()方法和enq()方法,我們還可以看到,AQS中的同步隊(duì)列是通過一個(gè)雙向鏈表來實(shí)現(xiàn)的。當(dāng)一個(gè)節(jié)點(diǎn)入隊(duì)和出隊(duì)時(shí),需要修改兩個(gè)指針(prev和next)。addWaiter()方法執(zhí)行后,我們通過下圖大致看一下此時(shí)同步隊(duì)列中指向的節(jié)點(diǎn)。在這里,如果不太理解,可以回頭看看enq()方法的執(zhí)行流程。執(zhí)行addWaiter()方法后,它將在入隊(duì)后返回到新節(jié)點(diǎn)。然后開始執(zhí)行acquireQueued()方法。這個(gè)方法做了五件事。獲取當(dāng)前節(jié)點(diǎn)的前任節(jié)點(diǎn)p。如果P是頭節(jié)點(diǎn),再嘗試獲取鎖,如果成功獲取鎖,就可以跳出循環(huán)了。無法獲米樂M6 M6米樂取鎖。由shouldParkAfterFailedAcquire()將waitSatus改為-1(為什么改為-1?原因可以在AQS的源代碼中找到,后續(xù)獲取鎖的過程會(huì)遵循這個(gè)邏輯)
4.ParkandChekingInterrupt()方法會(huì)阻塞當(dāng)前線程,同時(shí)可以返回當(dāng)前線程的中斷狀態(tài)errupted()會(huì)清除中斷標(biāo)志位)。經(jīng)過上面的操作,我們可以知道線沒有獲得鎖,被添加到同步隊(duì)列中,并阻塞了它??偨Y(jié)起來就是四個(gè)字:“入隊(duì)”“封”。此時(shí),我們的同步隊(duì)列也變成如下所示。執(zhí)行后與上述addWaiter()方法相比,只是thread 1節(jié)點(diǎn)的前任節(jié)點(diǎn),waitStaus設(shè)置為-1。釋放鎖定過程整個(gè)過程(僅考慮解鎖成功)可以總結(jié)為三件事。釋放鎖資源;喚醒同步隊(duì)列中頭節(jié)點(diǎn)之后的節(jié)點(diǎn)對(duì)應(yīng)的線被喚醒的線程試圖競(jìng)爭(zhēng)鎖。如果競(jìng)爭(zhēng)成功,則更新同步隊(duì)列(即頭節(jié)點(diǎn)出列)。當(dāng)?shù)谝粋€(gè)線)執(zhí)行自己的業(yè)務(wù)流程時(shí),它將釋放鎖。至此,我們來看一下解除鎖的過程。調(diào)用unlock()方法可以釋放鎖。需要注意的是,為了避免死鎖,這個(gè)方法的調(diào)用需要放在最后的代碼塊中。當(dāng)thread0釋放鎖時(shí),它調(diào)用release()方法,該方法主要做兩件事。調(diào)用tryRelease()方法來釋放鎖。在方法內(nèi)部,狀態(tài)被設(shè)置為0,exclusiveOwnerThread被setExclusiveOwnerThread()方法設(shè)置為null時(shí),意味著此時(shí)鎖資源被釋放,沒有線程持有鎖資源。如果第一步返回true,即鎖被成功釋放,那么開始第二步。第二步是首先獲取同步隊(duì)列的頭節(jié)點(diǎn),并檢查其waitStatus屬性。這里,讓我們把剛剛獲得鎖的節(jié)點(diǎn)的情況放在同步隊(duì)列中。此時(shí),我們的head節(jié)點(diǎn)的waitStatus為-1,所以我們將進(jìn)入unparksuccess()方法。parksuccess()方法主要做以下三件事。注意第三步。根據(jù)我們當(dāng)前的同步隊(duì)列,LockSupport.unpark()方法將喚醒線。當(dāng)執(zhí)行unparksuccess()方法中的LockSupport.unpark()方法時(shí),線)將被喚醒。線被喚醒后,我們來看看線的執(zhí)行情況。此時(shí),線將繼續(xù)執(zhí)行acquireQueued()方法中的for循環(huán)(注意:這是一個(gè)無限循環(huán))。執(zhí)行順序與鎖獲取相同,但當(dāng)與鎖獲取不同時(shí),步驟2中的鎖獲取將會(huì)成功(因?yàn)閠hread0已經(jīng)釋放了鎖)。成功獲取鎖后,當(dāng)前同步隊(duì)列中的頭節(jié)點(diǎn)將出隊(duì)。此時(shí),同步隊(duì)列中節(jié)點(diǎn)的情況如下。至此,釋放鎖的邏輯完成。其實(shí)總結(jié)起來很簡(jiǎn)單。首先釋放鎖資源,然后喚醒同步隊(duì)列中頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)對(duì)應(yīng)的線程,最后更新同步隊(duì)列(出列)。摘要以上是ReentrantLock獲取和釋放鎖的一般過程。通過本文,讀者對(duì)ReentrantLock的鎖的獲取和釋放過程有了一個(gè)大致的了解。細(xì)心的讀者可能會(huì)發(fā)現(xiàn),在獲取鎖時(shí),acquireQueued()方法中有一個(gè)cancelAcquire()方法的調(diào)用邏輯。
電話:13644723777
傳 真:+86-173-4169
手 機(jī):13644723777
郵 箱:mile@nmgdp.net
地 址:內(nèi)蒙古包頭市昆區(qū)昆工路光彩商業(yè)街99號(hào)