今天主要講AQS中對獨佔鎖的釋放,如果大家把昨天“獨佔鎖的獲取”看完了,今天這篇文章將會很輕鬆!
AQS在獨佔模式下,對鎖的釋放只有release方法,而release方法其實就做了兩件事:釋放鎖和喚醒後繼Node(準確講是Node中的線程,後面爲了方便統一稱爲Node)。下面我們直接看源碼吧
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
我們可以看到,裏面其實就只涉及到兩個方法,tryRelease和unparkSuccessor,分別對應釋放鎖和喚醒後繼Node,我們先梳理下release的整個邏輯再具體來看這兩個方法
我們首先會通過tryRelease來釋放鎖,如果釋放失敗,則會返回false,否則就會進入到if分支中,在if分支中,我們必須滿足(h != null && h.waitStatus != 0) 纔會去喚醒後繼Node。換句話說,我們只要滿足下麪條件中的一個,就不會去喚醒後繼Node
-
h==null
-
h != null && h.waitStatus == 0
第一點h == null 這個條件很好理解,如果頭結點爲空了,那麼同步隊列肯定也是空的,所以此時是沒有Node需要去喚醒的
第二點頭結點不爲空但狀態爲0是什麼情況呢?“併發三板斧”中我們說過,我們在Node初始化時,會把Node的狀態置爲0,我們再回想下昨天“獨佔鎖的獲取”中的enq方法,我們是在這裏將同步隊列初始化的(即生成head與tail),此時同步隊列中也是沒有等待喚醒的Node
最後需要注意的是,release方法的返回值只與釋放鎖有關,和喚醒線程是沒有關係的。好的,邏輯理清楚了,接下來就具體看下兩個方法吧
1 tryRelease
這個方法是留給子類實現的,用來釋放鎖,如果釋放成功則返回true,失敗返回false,這個具體的實現我們放在子類中講解,這裏就不拓展了
2 unparkSuccessor
此方法是喚醒後繼Node,我們看具體代碼:
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
如果結點的waitStatus<0,說明此時waitStatus爲SIGNAL(僅在獨佔模式下),我們此時需要將waitStatus置爲0,因爲待會會要喚醒後繼節點
接着,我們拿到這個Node的後繼Node,如果這個後繼Node爲空或者waitStatus屬性大於0,說明這個Node已經不存在或者已被取消,則我們需要從尾結點開始往前遍歷找到那個離head最近的需要被喚醒的Node,即進入第二個if分支
爲啥是要從後往前遍歷呢,這和我們“獨佔鎖的獲取”中講的入隊操作的順序有關。獨佔模式下調用addWaiter方法入隊時,是先將入隊Node的前驅Node指向此時的尾Node,然後再通過CAS將tail指向入隊Node,如果成功了(這步成功就標誌着此Node已經入隊成功,其他競爭的Node需要重新入隊),纔會將原來的尾Node的後繼Node指向入隊Node,代碼如下
node.prev = pred; //step1
if (compareAndSetTail(pred, node)) // step2
pred.next = node; // step3
假設我們此時有個Node正在入隊,執行完step2,還未執行step3,如果unparkSuccessor採用從head往後遍歷的話,此時是找不到這個新插入的Node的;但如果是採用從後往前遍歷,則不會出現這個問題。如果對這個不太理解,可以去我上一篇文章“獨佔鎖的獲取”看看addWaiter方法的講解,裏面畫了三幅圖可以幫助理解併發時的入隊情況,從而明白這裏爲什麼需要從後往前遍歷
最後遍歷完後,我們會進入第三個if判斷,如果s不爲空,說明s就是隊列中需要被喚醒的且最靠近head的節點,我們使用unpark方法將其喚醒就可以了
好了,獨佔鎖的釋放就講完了,是不是比起獨佔鎖的獲取簡單多了,當然,覺得簡單的前提是你已經理解了獨佔鎖的獲取了
(未完)
歡迎大家關注我的公衆號 “程序員進階之路”,裏面記錄了一個非科班程序員的成長之路