知識點
AQS維護著兩個隊列:
- 一個是由AQS類維護的CLH隊列(用于運行CLH算法)
- 另一個是由AQS的內(nèi)部類ConditionObject維護的Condition隊列(用于支持線程間的同步,提供await,signal,signalAll方法)。
關(guān)于鎖
- 鎖的釋放:通過持有鎖的線程對CLH隊列的下個節(jié)點調(diào)用
LockSupport.unpark(s.thread);- 加鎖:調(diào)用
LockSupport.park(this);將本線程阻塞
Node.SIGNAL有2個作用:
- 前繼節(jié)點為
SIGNAL時,后繼節(jié)點會被掛起- 前繼節(jié)點釋放鎖或被取消之后,必須喚醒(unparking)其后繼結(jié)點
共享模式和獨占模式在代碼上的區(qū)分點:
doAcquireShared(int arg)中:
int r = tryAcquireShared(arg); //r說明可以多線程共享鎖
setHeadAndPropagate(node, r); //顯示當(dāng)本節(jié)點獲得鎖后,還要通知后續(xù)節(jié)點繼續(xù)獲取鎖
AQS中對CLH算法的實現(xiàn)與標(biāo)準(zhǔn)的CLH算法有什么異同?
到這里已經(jīng)可以解答這個問題了。AQS到底在哪些地方變種CLH鎖算法?
- CLH是一種自旋鎖算法(在得到鎖之前會不停地自旋),而AQS會在幾次自旋失敗后就將線程阻塞,這是為了避免不必要地占用CPU;
- CLH是自旋在前繼節(jié)點的標(biāo)志位上的,而AQS是自旋在
p == head上面(即不停地判斷前繼節(jié)點是否是頭節(jié)點),只有在發(fā)現(xiàn)前繼節(jié)點是頭節(jié)點時,才會通過tryAcquire嘗試獲得鎖,這里有一個比較另我困惑的地方,就是head是一個volatile的全局引用,這么做的話顯然違背了CLH鎖的Local Spin的思想,具體原因未知,可能是因為AQS最初就是被設(shè)計為阻塞的同步器而不是自旋鎖吧。
Condition
Node類的nextWaiter字段其實是用來存放Condition隊列的后繼的,要和next字段(用來存放CLH隊列后繼)進行區(qū)分。
之所以Condition隊列和CLH隊列都采用Node類作為節(jié)點的原因就是為了方便將節(jié)點從Condition隊列搬運到CLH隊列。