1.背景
2.源碼解讀
調用該方法的地方
方法源碼解讀
/** * 取消獲取資源(異常處理時都需要用到) * 方法主要功能: * 1.處理當前取消節點的狀態; * 2.將當前取消節點的前置非取消節點和後置非取消節點"鏈接"起來; * 3.如果前置節點釋放了鎖,那麼當前取消節點承擔起後續節點的喚醒職責。 * * @param node */ private void cancelAcquire(Node node) { // 如果節點爲空直接返回 if (node == null) return; // 處理當前取消節點的狀態; node.thread = null; node.waitStatus = Node.CANCELLED; /* *這段代碼用來找到前置的非取消節點 */ Node pred = node.prev; while (pred.waitStatus > 0) { // pred.waitStatus > 0 說明當前節點的前置節點已取消,應該繼續向前找 pred = pred.prev; node.prev = pred; } /* *這裏注意一下: * 1.pred是一個實際有效的前節點,即前置的非取消節點 * 2.pred.next 並不一定是 node節點,因爲 while (pred.waitStatus > 0)前節點會向前移動 */ Node predNext = pred.next; // 修改尾指針:compareAndSetTail(node, pred),指向前置的第一個非取消節點; if (node == tail && compareAndSetTail(node, pred)) { // 將新的尾節點的next指針置空:compareAndSetNext(pred, predNext, null); // 爲什麼呢? 因爲node==tail,說明node節點的前一個非取消節點就是就會說最後一個節點,即無下一個節點 compareAndSetNext(pred, predNext, null); } else { int ws; // compareAndSetWaitStatus(pred, ws, Node.SIGNAL) 把前置節點設置爲下一個獲取鎖的節點 /* * 這個if的作用是:讓pred節點作爲下一個可以獲取鎖的節點 * 1.pred != head && pred.thread != null * 2. pred.waitStatus=-1 獲取 compareAndSetWaitStatus(pred, ws, Node.SIGNAL */ if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; if (next != null && next.waitStatus <= 0) // 設置前置節點的下一個節點爲:當前節點的下一個節點 compareAndSetNext(pred, predNext, next); } else { // 到這裏說明 pred是頭結點,喚醒node的下一個節點 unparkSuccessor(node); } node.next = node; // help GC } }
重點邏輯圖解說明
while (pred.waitStatus > 0) node.prev = pred = pred.prev;
假設當前隊列如下圖:
當前節點爲 t4前一個節點t3的waitStatus=1>0,會繼續向前找,最後會找到t2爲非取消的前置節點,循環結束
如果當節點是尾節點
/* *這裏注意一下: * 1.pred是一個實際有效的前節點,即前置的非取消節點 * 2.pred.next 並不一定是 node節點,因爲 while (pred.waitStatus > 0)前節點會向前移動 */ Node predNext = pred.next; // 修改尾指針:compareAndSetTail(node, pred),指向前置的第一個非取消節點; if (node == tail && compareAndSetTail(node, pred)) { // 將新的尾節點的next指針置空:compareAndSetNext(pred, predNext, null); // 爲什麼呢? 因爲node==tail,說明node節點的前一個非取消節點就是就會說最後一個節點,即無下一個節點 compareAndSetNext(pred, predNext, null); // 方法執行結束 }
代碼執行隊列圖:
當前節點不是尾節點的情況
int ws; // compareAndSetWaitStatus(pred, ws, Node.SIGNAL) 把前置節點設置爲下一個獲取鎖的節點 /* * 這個if的作用是:讓pred節點作爲下一個可以獲取鎖的節點 * 1.pred != head && pred.thread != null * 2. pred.waitStatus=-1 獲取 compareAndSetWaitStatus(pred, ws, Node.SIGNAL */ if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; if (next != null && next.waitStatus <= 0) // 設置前置節點的下一個節點爲:當前節點的下一個節點 compareAndSetNext(pred, predNext, next); } else { // 到這裏說明 pred是頭結點,喚醒node的下一個節點 unparkSuccessor(node); } node.next = node; // help GC
執行示意圖:
如果待取消的是t1節點,且t2節點是取消節點,則會直接執行unparkSuccessor(node);,這個過程會從tail指針開始從後往前,找到最靠近頭的有效(非取消)節點,喚醒這個線程。