AQS(AbstractQueuedSynchronizer)是一個(gè)用于構(gòu)建鎖和同步器的框架汁政,許多同步器都可以通過AQS很容易并且高效的構(gòu)造出來。不僅Reentrant和Semaphore是基于AQS構(gòu)建的,還包括CountDownLatch寒瓦、ReentrantReadWriteLock、SynchronousQueue和FutureTask。
高層抽象
在基于AQS構(gòu)建的同步器類中诗良,最基本的操作包括各種形式的獲取操作和釋放操作。
獲取操作是一種依賴狀態(tài)的操作鲁驶,并且通常會(huì)阻塞直到達(dá)到了理想狀態(tài)鉴裹。如:
- 使用ReentrantLock時(shí),“獲取”操作意味著“等待直到鎖可被獲取”。
- 使用Semaphore時(shí)径荔,“獲取”操作意味著“等待直到許可可被獲取”督禽。
- 使用CountDownLatch時(shí),“獲取”操作意味著“等待直到閉鎖達(dá)到結(jié)束狀態(tài)”总处。
- 使用FutureTask時(shí)狈惫,“獲取”操作意味著“等待直到任務(wù)完成”。
釋放并不是一個(gè)可阻塞的操作鹦马,當(dāng)執(zhí)行“釋放”操作時(shí)胧谈,所有在請求時(shí)被阻塞的線程都會(huì)開始執(zhí)行。
內(nèi)部原理
狀態(tài)機(jī)
AQS提供了一個(gè)高效的狀態(tài)機(jī)模型荸频,用來管理同步器類中的狀態(tài)和菱肖。
狀態(tài)
AQS使用一個(gè)整數(shù)state以表示狀態(tài),并通過getState旭从、setState及compareAndSetState等protected類型方法進(jìn)行狀態(tài)轉(zhuǎn)換稳强。巧妙的使用state,可以表示任何狀態(tài)和悦,如:
- ReentrantLock用state表示所有者線程已經(jīng)重復(fù)獲取該鎖的次數(shù)退疫。
- Semaphore用state表示剩余的許可數(shù)量。
- CountDownLatch用state表示閉鎖的狀態(tài)摹闽,如關(guān)閉蹄咖、打開。
- FutureTask用state表示任務(wù)的狀態(tài)付鹿,如尚未開始澜汤、正在運(yùn)行、已完成舵匾、已取消俊抵。
除了state,在同步器類中還可以自行管理一些額外的狀態(tài)變量坐梯。如:
- ReentrantLock保存了鎖的當(dāng)前所有者的信息徽诲,這樣就能區(qū)分某個(gè)獲取操作是重入的還是競爭的。
- FutureTask用result表示任務(wù)的結(jié)果吵血,該結(jié)果可能是計(jì)算得到的答案谎替,也可能是拋出的異常。
狀態(tài)轉(zhuǎn)換
狀態(tài)轉(zhuǎn)換則表現(xiàn)為不同的獲取操作和釋放操作蹋辅,其標(biāo)準(zhǔn)形式如下:
boolean acquire () throws InterruptedException {
while (當(dāng)前狀態(tài)不允許獲取操作) {
if (需要阻塞獲取請求) {
如果當(dāng)前線程不在隊(duì)列中钱贯,則將其插入隊(duì)列
阻塞當(dāng)前新城
}
else
返回失敗
}
可能更新同步器的狀態(tài)
如果當(dāng)前線程在隊(duì)列中,則將其移出隊(duì)列
返回成功
}
void release () {
更新同步器的狀態(tài)
if (新的狀態(tài)允許某個(gè)被阻塞的線程獲取成功)
接觸隊(duì)列中一個(gè)或多個(gè)線程的阻塞狀態(tài)
}
為什么10行只是“可能”侦另,而不是“必然”更新同步器的狀態(tài)呢秩命?因?yàn)楂@取同步器的某個(gè)線程可能對其他線程能否也獲取該同步器造成影響尉共,也可能不影響。如使用獨(dú)占的ReentrantLock時(shí)弃锐,一個(gè)線程獲取鎖后袄友,其他線程就不能再獲取鎖,于是需要更新同步器的狀態(tài)霹菊;但使用CountDownLatch時(shí)剧蚣,一個(gè)線程獲取閉鎖時(shí)(包括正在獲取和獲取后),不會(huì)影響其他線程能否獲取它浇辜,因此不需要更新同步器的狀態(tài)券敌。
一些約定
根據(jù)是否支持阻塞、是否支持獨(dú)占等柳洋,獲取操作和釋放操作都有多個(gè)實(shí)現(xiàn)待诅。獲取操作有acquire、acquireShared熊镣、tryAcquire卑雁、tryAcquireShared等,釋放操作有release绪囱、releaseShared测蹲、tryRelease、tryReleaseShared等鬼吵。
還有acquireNanos扣甲、acquireInterruptibly等實(shí)現(xiàn),為了講解方便齿椅,暫時(shí)忽略它們琉挖。
不帶try前綴的方法是阻塞的(當(dāng)然release、releaseShared不是可阻塞的)涣脚,通過調(diào)用帶try前綴的相應(yīng)版本實(shí)現(xiàn)示辈,如acquire內(nèi)部調(diào)用tryAcquire并維護(hù)相關(guān)邏輯。AQS抽象類中提供了不帶try前綴的方法遣蚀,并以final修飾矾麻,在實(shí)現(xiàn)同步器時(shí)應(yīng)直接使用;需要覆寫的是帶try前綴的方法芭梯。對于這些方法险耀,約定通過返回值告知調(diào)用者(一般是AQS)獲取或釋放操作是否成功,一些特殊的值代表額外的信息:
- 對于tryAcquire玖喘,如果返回true胰耗,則表示獲取成功;否則返回false芒涡。
- 對于tryAcquireShared柴灯,如果返回一個(gè)負(fù)值,那么表示獲取操作失敗费尽,返回零值表示同步器通過獨(dú)占方式被獲取赠群,返回正值表示同步器通過非獨(dú)占方式被獲取。
- 對于tryRelease與tryReleaseShared方法來說旱幼,如果返回true查描,則表示已完全釋放,所有在獲取同步器時(shí)被阻塞的線程都可以被恢復(fù)執(zhí)行柏卤;否則返回false冬三。
要想基于AQS構(gòu)建同步器,就必須對上述四個(gè)方法爛熟于心缘缚。
總結(jié)
- state表示狀態(tài)勾笆,其他狀態(tài)需要自行維護(hù)
- 直接使用不帶try前綴的方法,并覆寫帶try前綴的方法
- 對于帶try前綴的方法桥滨,約定通過返回值告知調(diào)用者(一般是AQS)獲取或釋放操作的結(jié)果
本文雖短窝爪,且非常重要,是后文分析ReentrantLock等的基礎(chǔ)齐媒。同時(shí)蒲每,又是一個(gè)高效并發(fā)的經(jīng)典設(shè)計(jì)案例。
本文鏈接:AQS的基本原理
作者:猴子007
出處:https://monkeysayhi.github.io
本文基于 知識(shí)共享署名-相同方式共享 4.0 國際許可協(xié)議發(fā)布喻括,歡迎轉(zhuǎn)載邀杏,演繹或用于商業(yè)目的,但是必須保留本文的署名及鏈接唬血。