并發(fā)工具類Condition介紹與源碼解析

在之前介紹AQS源碼的時(shí)候,還遺留了一個(gè)內(nèi)部類ConditionObject沒有介紹,它也是并發(fā)中至關(guān)重要的類。

主要作用

Condition主要提供多個(gè)await方法以及signal、signalAll方法,對(duì)標(biāo)的是Object的wait、notify、notifyAll方法,對(duì)應(yīng)的作用也一樣。

ConditionObject是AQS的內(nèi)部類并且實(shí)現(xiàn)了接口Condition,主要提供的功能也一樣。

創(chuàng)建方法

只有ReentrantLock提供了獲取AQS的Condition對(duì)象的方法。首先要new一個(gè)ReentrantLock對(duì)象,然后通過ReentrantLock的newCondition()方法獲取Condition對(duì)象,每次獲取都是創(chuàng)建一個(gè)新的Condition對(duì)象。

結(jié)構(gòu)與主要方法

Condition(表示AQS中的ConditionObject,下同)只有兩個(gè)屬性firstWaiter、lastWaiter,類型是AQS的Node,也就是Condition也維護(hù)一個(gè)鏈表,firstWaiter、lastWaiter分別是鏈表的首位節(jié)點(diǎn)。

await方法源碼分析

Condition提供了很多個(gè)await方法,主要區(qū)別在于是否在如果等待一定時(shí)間后不是否繼續(xù)等待,這里分析下await最基礎(chǔ)的無(wú)參方法,源代碼流程圖如下圖:

簡(jiǎn)單分析下await方法:

首先創(chuàng)建一個(gè)Node(當(dāng)前線程,狀態(tài)-2)節(jié)點(diǎn)并加到等待鏈表的尾部,這個(gè)Node是AQS中的Node,也會(huì)保存當(dāng)前線程;

第二步是釋放鎖,因?yàn)榫€程在等待過程中其他線程也需要使用lock,所以這里先釋放鎖,也就是修改AQS中state的值。

第三步是進(jìn)入一個(gè)while循環(huán),跳出循環(huán)判斷的條件是節(jié)點(diǎn)狀態(tài)不等于-2、并且要在同步隊(duì)列中(在AQS維護(hù)的鏈表中),如果沒在同步隊(duì)列中則會(huì)進(jìn)入循環(huán)掛起線程,當(dāng)再次被喚醒的時(shí)候會(huì)優(yōu)先判斷線程是否中斷,如果線程中斷也會(huì)跳出循環(huán),否則繼續(xù)循環(huán)判斷。

第四步是當(dāng)當(dāng)前線程跳出循環(huán)時(shí),會(huì)重新設(shè)置鎖,設(shè)置成功再去根據(jù)情況整理鏈表,如果線程是中斷的,則根據(jù)情況拋出中斷異常,或者中斷。

從源碼中可以得出,在調(diào)用await方法之前一定要先獲取到鎖,否則會(huì)拋出異常。

signal方法:讓線程跳出循環(huán)

知道了await方法的源碼流程,就可以猜測(cè)signal方法的源碼了,先來(lái)分析下await方法如何才能執(zhí)行完,先不考慮中斷線程的情況,要讓線程跳出while循環(huán)需要兩個(gè)條件:節(jié)點(diǎn)狀態(tài)不能是-2、讓節(jié)點(diǎn)在同步隊(duì)列中;記住這里的同步隊(duì)列是AQS維護(hù)的同步隊(duì)列。

所以現(xiàn)在再來(lái)看signal方法的源碼就簡(jiǎn)單了:

首先是拿到firstWaiter,如果為null則?繼續(xù)往鏈表后面找;

第二步是把firstWaiter的狀態(tài)從等待狀態(tài)改為0,如果修改失敗則直接返回false,說(shuō)明這個(gè)節(jié)點(diǎn)以及被修改成其他狀態(tài)了,繼續(xù)尋找下一個(gè)節(jié)點(diǎn)。

第三步把節(jié)點(diǎn)加到同步隊(duì)列的尾部;

第四步根據(jù)同步隊(duì)列的前置節(jié)點(diǎn)狀態(tài)判斷是否需要直接喚醒當(dāng)前節(jié)點(diǎn)中的線程;

signal方法就是把Condition中維護(hù)的鏈表節(jié)點(diǎn)的頭部節(jié)點(diǎn)狀態(tài)設(shè)置為0并且加到AQS的同步隊(duì)列中。signalAll方法就是把鏈表的所有節(jié)點(diǎn)走一下signal方法的流程。

Condition與Object

分析了Condition方法但是并沒有體現(xiàn)它相比于Object的wait、notify、notifyAll的優(yōu)勢(shì),實(shí)際上這個(gè)問題在之前的文章設(shè)計(jì)阻塞隊(duì)列中有對(duì)比,這里大概講一下,阻塞隊(duì)列有put與take方法,當(dāng)隊(duì)列滿了put方法會(huì)阻塞,當(dāng)隊(duì)列是空時(shí)take方法會(huì)阻塞。

用Object的wait、notifyAll方法來(lái)實(shí)現(xiàn)put、take方法的阻塞,在多線程情況下一個(gè)put方法獲取到鎖,那么所有的包括put方法和take方法都會(huì)阻塞,當(dāng)put方法執(zhí)行完成后會(huì)喚醒take線程,但同時(shí)put線程也會(huì)被喚醒來(lái)?yè)寛?zhí)行權(quán),但是如果此時(shí)隊(duì)列已滿,實(shí)際上所有的put線程都不應(yīng)該會(huì)喚醒。

如果采用ReentrantLock來(lái)new出兩個(gè)Condition來(lái)實(shí)現(xiàn),put方法如果發(fā)現(xiàn)隊(duì)列滿了則調(diào)用一個(gè)Condition的await方法來(lái)阻塞線程,另外一個(gè)來(lái)阻塞take方法,當(dāng)put方法執(zhí)行完后只用調(diào)用阻塞take方法中的Condition的signal,只用喚醒隊(duì)列中最前面一個(gè)就夠了。

總的來(lái)說(shuō)可以利用一個(gè)ReentrantLock來(lái)實(shí)現(xiàn)對(duì)資源的訪問控制,利用多個(gè)Condition來(lái)更加精準(zhǔn)的控制線程的阻塞與喚醒,相比利用一個(gè)Object來(lái)控制更加準(zhǔn)確,可以使程序運(yùn)行的更加高效。

總結(jié)

最后再梳理下Condition的await、signal方法,再使用await方法之前必須調(diào)用Condition對(duì)應(yīng)的ReentrantLock的lock方法,也就是必須要獲取鎖。線程要跳出await方法就必須把節(jié)點(diǎn)放到AQS中的同步隊(duì)列中。而signal方法就是把節(jié)點(diǎn)放到同步隊(duì)列中。

再整理下整個(gè)流程:lock方法直到獲取鎖、調(diào)用await方法(生成一個(gè)Node放到Condition鏈表的末尾)、然后釋放鎖、進(jìn)入while循環(huán)掛起線程、另外的線程獲取到鎖調(diào)用signal修改Condition鏈表的頭部節(jié)點(diǎn)狀態(tài)并放到AQS同步隊(duì)列中、當(dāng)其他線程釋放鎖會(huì)去喚醒AQS同步隊(duì)列頭部線程、如果這個(gè)節(jié)點(diǎn)被喚醒就繼續(xù)執(zhí)行await的while判斷會(huì)跳出循環(huán)、設(shè)置鎖狀態(tài)await方法執(zhí)行完成、最后再調(diào)用unlock方法釋放鎖。

ReentrantLock用來(lái)維護(hù)同步隊(duì)列保證只有一個(gè)線程訪問資源、await方法把保存線程的節(jié)點(diǎn)加到Condition維護(hù)的鏈表中,signal方法把節(jié)點(diǎn)加到同步隊(duì)列中,通過這樣無(wú)論是await方法被喚醒還是其他地方ReentrantLock調(diào)用了lock都能保證資源只被最多一個(gè)線程訪問。

ReentrantLock與Condition在其他并發(fā)類中經(jīng)常遇到,值得弄得他們的原理與優(yōu)勢(shì),便于理解其他并發(fā)類。

Java程序員日常學(xué)習(xí)筆記,如理解有誤歡迎各位交流討論!


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容