隊(duì)列同步器AbstractQueuedSynchronizer(以下簡(jiǎn)稱同步器)涮阔,是用來(lái)構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架窿锉,它使用了一個(gè)int成員變量表示同步狀態(tài)起意,通過(guò)內(nèi)置的FIFO隊(duì)列來(lái)完成資源獲取線程的排隊(duì)工作凌彬,并發(fā)包的作者(Doug?Lea)期望它能夠成為實(shí)現(xiàn)大部分同步需求的基礎(chǔ)把介。
同步器的主要使用方式是繼承,子類通過(guò)繼承同步器并實(shí)現(xiàn)它的抽象方法來(lái)管理同步狀態(tài)厢漩,在抽象方法的實(shí)現(xiàn)過(guò)程中免不了要對(duì)同步狀態(tài)進(jìn)行更改膜眠,這時(shí)就需要使用同步器提供的3個(gè)方法(getState()、setState(int?newState)和compareAndSetState(int?expect,int?update))來(lái)進(jìn)行操作溜嗜,因?yàn)樗鼈兡軌虮WC狀態(tài)的改變是安全的宵膨。子類推薦被定義為自定義同步組件的靜態(tài)內(nèi)部類,同步器自身沒(méi)有實(shí)現(xiàn)任何同步接口炸宵,它僅僅是定義了若干同步狀態(tài)獲取和釋放的方法來(lái)供自定義同步組件使用辟躏,同步器既可以支持獨(dú)占式地獲取同步狀態(tài),也可以支持共享式地獲取同步狀態(tài)土全,這樣就可以方便實(shí)現(xiàn)不同類型的同步組件(ReentrantLock捎琐、?ReentrantReadWriteLock和CountDownLatch等)。
同步器是實(shí)現(xiàn)鎖(也可以是任意同步組件)的關(guān)鍵裹匙,在鎖的實(shí)現(xiàn)中聚合同步器瑞凑,利用同步器實(shí)現(xiàn)鎖的語(yǔ)義「乓常可以這樣理解二者之間的關(guān)系:鎖是面向使用者的籽御,它定義了使用者與鎖交互的接口(比如可以允許兩個(gè)線程并行訪問(wèn)),隱藏了實(shí)現(xiàn)細(xì)節(jié)惰匙;同步器面向的是鎖的實(shí)現(xiàn)者技掏,它簡(jiǎn)化了鎖的實(shí)現(xiàn)方式,屏蔽了同步狀態(tài)管理徽曲、線程的排隊(duì)零截、等待與喚醒等底層操作。鎖和同步器很好地隔離了使用者和實(shí)現(xiàn)者所需關(guān)注的領(lǐng)域秃臣。
隊(duì)列同步器的接口與示例
同步器的設(shè)計(jì)是基于模板方法模式的涧衙,也就是說(shuō),使用者需要繼承同步器并重寫(xiě)指定的方法奥此,隨后將同步器組合在自定義同步組件的實(shí)現(xiàn)中弧哎,并調(diào)用同步器提供的模板方法,而這些模板方法將會(huì)調(diào)用使用者重寫(xiě)的方法稚虎。
重寫(xiě)同步器指定的方法時(shí)撤嫩,需要使用同步器提供的如下3個(gè)方法來(lái)訪問(wèn)或修改同步狀態(tài)。
·getState():獲取當(dāng)前同步狀態(tài)蠢终。
·setState(int?newState):設(shè)置當(dāng)前同步狀態(tài)序攘。
·compareAndSetState(int?expect,int?update):使用CAS設(shè)置當(dāng)前狀態(tài)茴她,該方法能夠保證狀態(tài)設(shè)置的原子性。
同步器可重寫(xiě)的方法與描述如表所示程奠。
實(shí)現(xiàn)自定義同步組件時(shí)丈牢,將會(huì)調(diào)用同步器提供的模板方法,這些(部分)模板方法與描述如表所示瞄沙。
同步器提供的模板方法基本上分為3類:獨(dú)占式獲取與釋放同步狀態(tài)己沛、共享式獲取與釋放同步狀態(tài)和查詢同步隊(duì)列中的等待線程情況。自定義同步組件將使用同步器提供的模板方法?來(lái)實(shí)現(xiàn)自己的同步語(yǔ)義距境。
只有掌握了同步器的工作原理才能更加深入地理解并發(fā)包中其他的并發(fā)組件申尼,所以下面通過(guò)一個(gè)獨(dú)占鎖的示例來(lái)深入了解一下同步器的工作原理。
顧名思義垫桂,獨(dú)占鎖就是在同一時(shí)刻只能有一個(gè)線程獲取到鎖师幕,而其他獲取鎖的線程只能處于同步隊(duì)列中等待,只有獲取鎖的線程釋放了鎖诬滩,后繼的線程才能夠獲取鎖们衙,如代碼所示。
上述示例中碱呼,獨(dú)占鎖Mutex是一個(gè)自定義同步組件,它在同一時(shí)刻只允許一個(gè)線程占有鎖宗侦。Mutex中定義了一個(gè)靜態(tài)內(nèi)部類愚臀,該內(nèi)部類繼承了同步器并實(shí)現(xiàn)了獨(dú)占式獲取和釋放同步?狀態(tài)。在tryAcquire(int?acquires)方法中矾利,如果經(jīng)過(guò)CAS設(shè)置成功(同步狀態(tài)設(shè)置為1姑裂,則代表獲取了同步狀態(tài),而在tryRelease(int?releases)方法中只是將同步狀態(tài)重置為0男旗。用戶使用Mutex時(shí)并不會(huì)直接和內(nèi)部同步器的實(shí)現(xiàn)打交道舶斧,而是調(diào)用Mutex提供的方法,在Mutex的實(shí)現(xiàn)中察皇,以獲取鎖的lock()方法為例茴厉,只需要在方法實(shí)現(xiàn)中調(diào)用同步器的模板方法acquire(int?args)即可,當(dāng)前線程調(diào)用該方法獲取同步狀態(tài)失敗后會(huì)被加入到同步隊(duì)列中等待什荣,這樣就大大降低了實(shí)現(xiàn)一個(gè)可靠自定義同步組件的門(mén)檻矾缓。
隊(duì)列同步器的實(shí)現(xiàn)分析
接下來(lái)將從實(shí)現(xiàn)角度分析同步器是如何完成線程同步的,主要包括:同步隊(duì)列稻爬、獨(dú)占式同步狀態(tài)獲取與釋放嗜闻、共享式同步狀態(tài)獲取與釋放以及超時(shí)獲取同步狀態(tài)等同步器的核心數(shù)據(jù)結(jié)構(gòu)與模板方法。
1.同步隊(duì)列
同步器依賴內(nèi)部的同步隊(duì)列(一個(gè)FIFO雙向隊(duì)列)來(lái)完成同步狀態(tài)的管理桅锄,當(dāng)前線程獲取同步狀態(tài)失敗時(shí)琉雳,同步器會(huì)將當(dāng)前線程以及等待狀態(tài)等信息構(gòu)造成為一個(gè)節(jié)點(diǎn)(Node)并將其加入同步隊(duì)列样眠,同時(shí)會(huì)阻塞當(dāng)前線程,當(dāng)同步狀態(tài)釋放時(shí)翠肘,會(huì)把首節(jié)點(diǎn)中的線程喚醒檐束,使其再次嘗試獲取同步狀態(tài)。
同步隊(duì)列中的節(jié)點(diǎn)(Node)用來(lái)保存獲取同步狀態(tài)失敗的線程引用锯茄、等待狀態(tài)以及前驅(qū)和后繼節(jié)點(diǎn)厢塘,節(jié)點(diǎn)的屬性類型與名稱以及描述如表所示。
節(jié)點(diǎn)是構(gòu)成同步隊(duì)列(等待隊(duì)列)的基礎(chǔ)肌幽,同步器擁有首節(jié)點(diǎn)(head)?和尾節(jié)點(diǎn)(tail)晚碾,沒(méi)有成功獲取同步狀態(tài)的線程將會(huì)成為節(jié)點(diǎn)加入該隊(duì)列的尾部,同步隊(duì)列的基本結(jié)構(gòu)如圖所示喂急。
在上圖中格嘁,同步器包含了兩個(gè)節(jié)點(diǎn)類型的引用,一個(gè)指向頭節(jié)點(diǎn)廊移,而另一個(gè)指向尾節(jié)點(diǎn)糕簿。試想一下,當(dāng)一個(gè)線程成功地獲取了同步狀態(tài)(或者鎖)狡孔,其他線程將無(wú)法獲取到同步狀態(tài)懂诗,轉(zhuǎn)而被構(gòu)造成為節(jié)點(diǎn)并加入到同步隊(duì)列中,而這個(gè)加入隊(duì)列的過(guò)程必須要保證線程安全苗膝,因此同步器提供了一個(gè)基于CAS的設(shè)置尾節(jié)點(diǎn)的方法:compareAndSetTail(Node???????expect,Node?update)殃恒,它需要傳遞當(dāng)前線程“認(rèn)為”的尾節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn),只有設(shè)置成功后辱揭,當(dāng)前節(jié)點(diǎn)才正式與之前的尾節(jié)點(diǎn)建立關(guān)聯(lián)离唐。
同步器將節(jié)點(diǎn)加入到同步隊(duì)列的過(guò)程如圖所示。
同步隊(duì)列遵循FIFO问窃,首節(jié)點(diǎn)是獲取同步狀態(tài)成功的節(jié)點(diǎn)亥鬓,首節(jié)點(diǎn)的線程在釋放同步狀態(tài)時(shí),將會(huì)喚醒后繼節(jié)點(diǎn)域庇,而后繼節(jié)點(diǎn)將會(huì)在獲取同步狀態(tài)成功時(shí)將自己設(shè)置為首節(jié)點(diǎn)嵌戈,該過(guò)程如圖所示。
在上圖中较剃,設(shè)置首節(jié)點(diǎn)是通過(guò)獲取同步狀態(tài)成功的線程來(lái)完成的咕别,由于只有一個(gè)線程能夠成功獲取到同步狀態(tài),因此設(shè)置頭節(jié)點(diǎn)的方法并不需要使用CAS來(lái)保證写穴,它只需要將首節(jié)點(diǎn)設(shè)置成為原首節(jié)點(diǎn)的后繼節(jié)點(diǎn)并斷開(kāi)原首節(jié)點(diǎn)的next引用即可惰拱。
獨(dú)占式同步狀態(tài)獲取與釋放
通過(guò)調(diào)用同步器的acquire(int?arg)方法可以獲取同步狀態(tài),該方法對(duì)中斷不敏感,也就是由于線程獲取同步狀態(tài)失敗后進(jìn)入同步隊(duì)列中偿短,后續(xù)對(duì)線程進(jìn)行中斷操作時(shí)欣孤,線程不會(huì)從同步隊(duì)列中移出,該方法代碼如下所示昔逗。
上述代碼主要完成了同步狀態(tài)獲取降传、節(jié)點(diǎn)構(gòu)造、加入同步隊(duì)列以及在同步隊(duì)列中自旋等待的相關(guān)工作勾怒,其主要邏輯是:首先調(diào)用自定義同步器實(shí)現(xiàn)的tryAcquire(int??arg)方法婆排,該方法保證線程安全的獲取同步狀態(tài),如果同步狀態(tài)獲取失敗笔链,則構(gòu)造同步節(jié)點(diǎn)(獨(dú)占式?Node.EXCLUSIVE段只,同一時(shí)刻只能有一個(gè)線程成功獲取同步狀態(tài))并通過(guò)addWaiter(Node???????node)?方法將該節(jié)點(diǎn)加入到同步隊(duì)列的尾部,最后調(diào)用acquireQueued(Node?node,int?arg)方法鉴扫,使得該節(jié)點(diǎn)以“死循環(huán)”的方式獲取同步狀態(tài)赞枕。如果獲取不到則阻塞節(jié)點(diǎn)中的線程,而被阻塞線程的喚醒主要依靠前驅(qū)節(jié)點(diǎn)的出隊(duì)或阻塞線程被中斷來(lái)實(shí)現(xiàn)坪创。
下面分析一下相關(guān)工作炕婶。首先是節(jié)點(diǎn)的構(gòu)造以及加入同步隊(duì)列,如下代碼所示莱预。
上述代碼通過(guò)使用compareAndSetTail(Node?expect,Node?update)方法來(lái)確保節(jié)點(diǎn)能夠被線程安全添加柠掂。試想一下:如果使用一個(gè)普通的LinkedList來(lái)維護(hù)節(jié)點(diǎn)之間的關(guān)系,那么當(dāng)一個(gè)線程獲取了同步狀態(tài)依沮,而其他多個(gè)線程由于調(diào)用tryAcquire(int?arg)方法獲取同步狀態(tài)失敗而并發(fā)地被添加到LinkedList時(shí)陪踩,LinkedList將難以保證Node的正確添加,最終的結(jié)果可能是節(jié)點(diǎn)的數(shù)量有偏差悉抵,而且順序也是混亂的。
在enq(final?Node?node)方法中摘完,同步器通過(guò)“死循環(huán)”來(lái)保證節(jié)點(diǎn)的正確添加姥饰,在“死循環(huán)”中只有通過(guò)CAS將節(jié)點(diǎn)設(shè)置成為尾節(jié)點(diǎn)之后,當(dāng)前線程才能從該方法返回孝治,否則列粪,當(dāng)前線程不斷地嘗試設(shè)置√胳可以看出岂座,enq(final?Node?node)方法將并發(fā)添加節(jié)點(diǎn)的請(qǐng)求通過(guò)CAS變得“串行化”了。
節(jié)點(diǎn)進(jìn)入同步隊(duì)列之后杭措,就進(jìn)入了一個(gè)自旋的過(guò)程费什,每個(gè)節(jié)點(diǎn)(或者說(shuō)每個(gè)線程)都在自省地觀察,當(dāng)條件滿足手素,獲取到了同步狀態(tài)鸳址,就可以從這個(gè)自旋過(guò)程中退出瘩蚪,否則依舊留在這個(gè)自旋過(guò)程中(并會(huì)阻塞節(jié)點(diǎn)的線程),如代碼所示稿黍。
在acquireQueued(final?Node?node,int?arg)方法中疹瘦,當(dāng)前線程在“死循環(huán)”中嘗試獲取同步狀態(tài),而只有前驅(qū)節(jié)點(diǎn)是頭節(jié)點(diǎn)才能夠嘗試獲取同步狀態(tài)巡球,這是為什么言沐?原因有兩個(gè),如下酣栈。
第一险胰,頭節(jié)點(diǎn)是成功獲取到同步狀態(tài)的節(jié)點(diǎn),而頭節(jié)點(diǎn)的線程釋放了同步狀態(tài)之后钉嘹,將會(huì)
喚醒其后繼節(jié)點(diǎn)鸯乃,后繼節(jié)點(diǎn)的線程被喚醒后需要檢查自己的前驅(qū)節(jié)點(diǎn)是否是頭節(jié)點(diǎn)。
第二跋涣,維護(hù)同步隊(duì)列的FIFO原則缨睡。該方法中,節(jié)點(diǎn)自旋獲取同步狀態(tài)的行為如圖所示陈辱。
在上圖中奖年,由于非首節(jié)點(diǎn)線程前驅(qū)節(jié)點(diǎn)出隊(duì)或者被中斷而從等待狀態(tài)返回,隨后檢查自己的前驅(qū)是否是頭節(jié)點(diǎn)沛贪,如果是則嘗試獲取同步狀態(tài)陋守。可以看到節(jié)點(diǎn)和節(jié)點(diǎn)之間在循環(huán)檢查的過(guò)程中基本不相互通信利赋,而是簡(jiǎn)單地判斷自己的前驅(qū)是否為頭節(jié)點(diǎn)水评,這樣就使得節(jié)點(diǎn)的釋放規(guī)則符合FIFO,并且也便于對(duì)過(guò)早通知的處理(過(guò)早通知是指前驅(qū)節(jié)點(diǎn)不是頭節(jié)點(diǎn)的線程由于中斷而被喚醒)媚送。
獨(dú)占式同步狀態(tài)獲取流程中燥,也就是acquire(int?arg)方法調(diào)用流程,如圖所示塘偎。
在上圖中疗涉,前驅(qū)節(jié)點(diǎn)為頭節(jié)點(diǎn)且能夠獲取同步狀態(tài)的判斷條件和線程進(jìn)入等待狀態(tài)是獲取同步狀態(tài)的自旋過(guò)程。當(dāng)同步狀態(tài)獲取成功之后吟秩,當(dāng)前線程從acquire(int?arg)方法返回咱扣,如果對(duì)于鎖這種并發(fā)組件而言,代表著當(dāng)前線程獲取了鎖涵防。
當(dāng)前線程獲取同步狀態(tài)并執(zhí)行了相應(yīng)邏輯之后闹伪,就需要釋放同步狀態(tài),使得后續(xù)節(jié)點(diǎn)能夠繼續(xù)獲取同步狀態(tài)。通過(guò)調(diào)用同步器的release(int?arg)方法可以釋放同步狀態(tài)祭往,該方法在釋放了同步狀態(tài)之后伦意,會(huì)喚醒其后繼節(jié)點(diǎn)(進(jìn)而使后繼節(jié)點(diǎn)重新嘗試獲取同步狀態(tài))。該方法代碼如下所示硼补。
該方法執(zhí)行時(shí)驮肉,會(huì)喚醒頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)線程,unparkSuccessor(Node?node)方法使用?LockSupport來(lái)喚醒處于等待狀態(tài)的線程已骇。
分析了獨(dú)占式同步狀態(tài)獲取和釋放過(guò)程后离钝,適當(dāng)做個(gè)總結(jié):在獲取同步狀態(tài)時(shí),同步器維護(hù)一個(gè)同步隊(duì)列褪储,獲取狀態(tài)失敗的線程都會(huì)被加入到隊(duì)列中并在隊(duì)列中進(jìn)行自旋卵渴;移出隊(duì)列
(或停止自旋)的條件是前驅(qū)節(jié)點(diǎn)為頭節(jié)點(diǎn)且成功獲取了同步狀態(tài)。在釋放同步狀態(tài)時(shí)鲤竹,同步器調(diào)用tryRelease(int?arg)方法釋放同步狀態(tài)浪读,然后喚醒頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)。
共享式同步狀態(tài)獲取與釋放
共享式獲取與獨(dú)占式獲取最主要的區(qū)別在于同一時(shí)刻能否有多個(gè)線程同時(shí)獲取到同步狀態(tài)辛藻。以文件的讀寫(xiě)為例碘橘,如果一個(gè)程序在對(duì)文件進(jìn)行讀操作,那么這一時(shí)刻對(duì)于該文件的寫(xiě)操作均被阻塞吱肌,而讀操作能夠同時(shí)進(jìn)行痘拆。寫(xiě)操作要求對(duì)資源的獨(dú)占式訪問(wèn)烂翰,而讀操作可以是共享式訪問(wèn)叫搁,兩種不同的訪問(wèn)模式在同一時(shí)刻對(duì)文件或資源的訪問(wèn)情況,如圖所示污筷。
在上圖中规揪,左半部分桥氏,共享式訪問(wèn)資源時(shí),其他共享式的訪問(wèn)均被允許猛铅,而獨(dú)占式訪問(wèn)被阻塞识颊,右半部分是獨(dú)占式訪問(wèn)資源時(shí),同一時(shí)刻其他訪問(wèn)均被阻塞奕坟。
通過(guò)調(diào)用同步器的acquireShared(int?arg)方法可以共享式地獲取同步狀態(tài),該方法代碼如下所示清笨。
在acquireShared(int?arg)方法中月杉,同步器調(diào)用tryAcquireShared(int?arg)方法嘗試獲取同步狀態(tài),tryAcquireShared(int?arg)方法返回值為int類型抠艾,當(dāng)返回值大于等于0時(shí)苛萎,表示能夠獲取到同步狀態(tài)。因此,在共享式獲取的自旋過(guò)程中腌歉,成功獲取到同步狀態(tài)并退出自旋的條件就是?tryAcquireShared(int?arg)方法返回值大于等于0蛙酪。可以看到翘盖,在doAcquireShared(int?arg)方法的自旋過(guò)程中桂塞,如果當(dāng)前節(jié)點(diǎn)的前驅(qū)為頭節(jié)點(diǎn)時(shí),嘗試獲取同步狀態(tài)馍驯,如果返回值大于等于0阁危,表示該次獲取同步狀態(tài)成功并從自旋過(guò)程中退出。
與獨(dú)占式一樣汰瘫,共享式獲取也需要釋放同步狀態(tài)狂打,通過(guò)調(diào)用releaseShared(int?arg)方法可以
釋放同步狀態(tài),該方法代碼如下所示混弥。
該方法在釋放同步狀態(tài)之后趴乡,將會(huì)喚醒后續(xù)處于等待狀態(tài)的節(jié)點(diǎn)。對(duì)于能夠支持多個(gè)線程同時(shí)訪問(wèn)的并發(fā)組件(比如Semaphore)蝗拿,它和獨(dú)占式主要區(qū)別在于tryReleaseShared(int?arg)?方法必須確保同步狀態(tài)(或者資源數(shù))線程安全釋放晾捏,一般是通過(guò)循環(huán)和CAS來(lái)保證的,因?yàn)獒尫磐綘顟B(tài)的操作會(huì)同時(shí)來(lái)自多個(gè)線程蛹磺。
獨(dú)占式超時(shí)獲取同步狀態(tài)
通過(guò)調(diào)用同步器的doAcquireNanos(int?arg,long?nanosTimeout)方法可以超時(shí)獲取同步狀態(tài)粟瞬,即在指定的時(shí)間段內(nèi)獲取同步狀態(tài),如果獲取到同步狀態(tài)則返回true萤捆,否則裙品,返回false。該方法提供了傳統(tǒng)Java同步操作(比如synchronized關(guān)鍵字)所不具備的特性俗或。
在分析該方法的實(shí)現(xiàn)前市怎,先介紹一下響應(yīng)中斷的同步狀態(tài)獲取過(guò)程。在Java?5之前辛慰,當(dāng)一個(gè)線程獲取不到鎖而被阻塞在synchronized之外時(shí)区匠,對(duì)該線程進(jìn)行中斷操作,此時(shí)該線程的中斷標(biāo)志位會(huì)被修改帅腌,但線程依舊會(huì)阻塞在synchronized上驰弄,等待著獲取鎖。在Java?5中速客,同步器提供了acquireInterruptibly(int?arg)方法戚篙,這個(gè)方法在等待獲取同步狀態(tài)時(shí),如果當(dāng)前線程被中斷溺职,會(huì)立刻返回岔擂,并拋出InterruptedException位喂。
超時(shí)獲取同步狀態(tài)過(guò)程可以被視作響應(yīng)中斷獲取同步狀態(tài)過(guò)程的“增強(qiáng)版”,?doAcquireNanos(int?arg,long?nanosTimeout)方法在支持響應(yīng)中斷的基礎(chǔ)上乱灵,增加了超時(shí)獲取的特性塑崖。針對(duì)超時(shí)獲取,主要需要計(jì)算出需要睡眠的時(shí)間間隔nanosTimeout痛倚,為了防止過(guò)早通知规婆,?nanosTimeout計(jì)算公式為:nanosTimeout-=now-lastTime,其中now為當(dāng)前喚醒時(shí)間状原,lastTime為上次喚醒時(shí)間聋呢,如果nanosTimeout大于0則表示超時(shí)時(shí)間未到,需要繼續(xù)睡眠nanosTimeout納秒颠区,反之削锰,表示已經(jīng)超時(shí),該方法代碼如下所示毕莱。
該方法在自旋過(guò)程中器贩,當(dāng)節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)為頭節(jié)點(diǎn)時(shí)嘗試獲取同步狀態(tài),如果獲取成功則從該方法返回朋截,這個(gè)過(guò)程和獨(dú)占式同步獲取的過(guò)程類似蛹稍,但是在同步狀態(tài)獲取失敗的處理上有所不同。如果當(dāng)前線程獲取同步狀態(tài)失敗部服,則判斷是否超時(shí)(nanosTimeout小于等于0表示已經(jīng)超時(shí))唆姐,如果沒(méi)有超時(shí),重新計(jì)算超時(shí)間隔nanosTimeout廓八,然后使當(dāng)前線程等待?nanosTimeout納秒(當(dāng)已到設(shè)置的超時(shí)時(shí)間奉芦,該線程會(huì)從LockSupport.parkNanos(Object?blocker,long?nanos)方法返回)。
如果nanosTimeout小于等于spinForTimeoutThreshold(1000納秒)時(shí)剧蹂,將不會(huì)使該線程進(jìn)行超時(shí)等待声功,而是進(jìn)入快速的自旋過(guò)程。原因在于宠叼,非常短的超時(shí)等待無(wú)法做到十分精確先巴,如果這時(shí)再進(jìn)行超時(shí)等待,相反會(huì)讓nanosTimeout的超時(shí)從整體上表現(xiàn)得反而不精確冒冬。因此伸蚯,在超時(shí)非常短的場(chǎng)景下,同步器會(huì)進(jìn)入無(wú)條件的快速自旋简烤。
獨(dú)占式超時(shí)獲取同步態(tài)的流程如圖所示剂邮。
從圖中可以看出,獨(dú)占式超時(shí)獲取同步狀態(tài)doAcquireNanos(int?arg,long?nanosTimeout)?和獨(dú)占式獲取同步狀態(tài)acquire(int?args)在流程上非常相似乐埠,其主要區(qū)別在于未獲取到同步狀態(tài)時(shí)的處理邏輯抗斤。acquire(int?args)在未獲取到同步狀態(tài)時(shí),將會(huì)使當(dāng)前線程一直處于等待狀態(tài)丈咐,而doAcquireNanos(int?arg,long?nanosTimeout)會(huì)使當(dāng)前線程等待nanosTimeout納秒瑞眼,如果當(dāng)?前線程在nanosTimeout納秒內(nèi)沒(méi)有獲取到同步狀態(tài),將會(huì)從等待邏輯中自動(dòng)返回棵逊。