AQS核心流程解析cancelAcquire方法
引出問(wèn)題
首先,先考慮一個(gè)問(wèn)題,什么條件會(huì)觸發(fā)cancelAcquire()方法?

cancelAcquire()方法的反向查找
可以清楚的看到在互斥鎖和共享鎖的拿鎖過(guò)程中都是有調(diào)用此方法的,而cancelAcquire()方法是寫(xiě)在finally代碼塊中,并且使用failed標(biāo)志位來(lái)控制cancelAcquire()方法的執(zhí)行。可以得出,在觸發(fā)異常的情況下會(huì)執(zhí)行cancelAcquire()方法。

響應(yīng)中斷的獲鎖方法
可以清楚的看到,這里是響應(yīng)異常,如果發(fā)生了異常,比如中斷異常,那么當(dāng)前線(xiàn)程N(yùn)ode需要做出取消的操作,那么下面詳細(xì)的說(shuō)明cancelAcquire()方法。

private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
// 當(dāng)前節(jié)點(diǎn)的線(xiàn)程指向置為null
node.thread = null;
// 協(xié)同取消的處理。
// 這里是判斷當(dāng)前節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)的狀態(tài)是否是取消狀態(tài)(狀態(tài)大于0只有是取消狀態(tài))
// 如果上一個(gè)節(jié)點(diǎn)是取消狀態(tài),那么繼續(xù)往上遍歷,直到找到狀態(tài)為小于0的狀態(tài)節(jié)點(diǎn)。
// 并且把當(dāng)前節(jié)點(diǎn)的prev指向非取消節(jié)點(diǎn)。
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// 得到?jīng)]有取消節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)。
Node predNext = pred.next;
// 因?yàn)楫?dāng)前cancelAcquire()方法就是取消的處理
// 所以將當(dāng)前節(jié)點(diǎn)設(shè)置為取消狀態(tài)。
node.waitStatus = Node.CANCELLED;
// 如果當(dāng)前取消的節(jié)點(diǎn)是tail節(jié)點(diǎn),也就是最后一個(gè)節(jié)點(diǎn)
// 那么就把tail指針指向上面while循環(huán)遍歷出的prev節(jié)點(diǎn)(因?yàn)橐赶蛞粋€(gè)沒(méi)有被取消的節(jié)點(diǎn))。
if (node == tail && compareAndSetTail(node, pred)) {
// help GC
// 為什么說(shuō)help GC呢?
// 因?yàn)榘裵rev的next節(jié)點(diǎn)設(shè)置為null,
// 這樣GC ROOT掃描發(fā)現(xiàn)沒(méi)有根節(jié)點(diǎn)的引用。
compareAndSetNext(pred, predNext, null);
} else {
// 走到else代表當(dāng)前節(jié)點(diǎn)不是tail節(jié)點(diǎn),或者是cas操作的時(shí)候tail發(fā)生了變化
// 如果不是tail節(jié)點(diǎn),不能直接把tail節(jié)點(diǎn)指向到上面while循環(huán)得出的prev節(jié)點(diǎn)
int ws;
// 這里是的if代碼塊,是為了嘗試一次,如果不成功再去復(fù)雜的處理。
// 這里的if判斷條件如下:
// 1.如果上面while循環(huán)得到的prev節(jié)點(diǎn)不是head節(jié)點(diǎn)
// 2.如果上面while循環(huán)得到的prev節(jié)點(diǎn)為-1,如果不為-1,cas改變成-1也。
// 3.如果上面while循環(huán)得到的rpev節(jié)點(diǎn)的線(xiàn)程指向不為null(如果為null代表在取消的過(guò)程中)
// 因?yàn)?amp;&是拼接,所以上面任意一個(gè)條件為false就會(huì)進(jìn)入到else條件中。
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
// 進(jìn)到這里代表這次嘗試成功了。
// 得到當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)
// 然后把前面while循環(huán)得到的prev節(jié)點(diǎn)的next指向當(dāng)前節(jié)點(diǎn)的next節(jié)點(diǎn)。
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
// 直接喚醒當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)。
// 喚醒的目的是為了去執(zhí)行shouldParkAfterFailedAcquire方法去處理取消節(jié)點(diǎn)。
unparkSuccessor(node);
}
// 把當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)指向自己.
// help gc。
node.next = node; // help GC
}
}比較復(fù)雜,所以筆者為了讀者的觀看順利, 下面會(huì)拆分步驟,并且畫(huà)圖來(lái)理解。
跳過(guò)已經(jīng)取消的節(jié)點(diǎn),找到一個(gè)非取消的節(jié)點(diǎn)
// Skip cancelled predecessors
// 跳過(guò)已經(jīng)被取消的節(jié)點(diǎn)
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
更新正常節(jié)點(diǎn)的鏈表
當(dāng)前取消節(jié)點(diǎn)是tail節(jié)點(diǎn)的情況
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
// 后面講
}
當(dāng)前取消節(jié)點(diǎn)是非tail節(jié)點(diǎn)的情況
// 當(dāng)前取消的節(jié)點(diǎn)不為tail節(jié)點(diǎn)的情況
int ws;
// if的邏輯可以理解為嘗試一次。
// 代表while循環(huán)得到的prev節(jié)點(diǎn)不是head節(jié)點(diǎn)
// 代表while循環(huán)得到的prev節(jié)點(diǎn)是可喚醒的正常節(jié)點(diǎn)
// 節(jié)點(diǎn)while循環(huán)得到的prev節(jié)點(diǎn)不是待取消節(jié)點(diǎn)
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
// 當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)也是正常的情況(非取消)
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
// 嘗試失敗,只能走比較狠的邏輯去處理了。
unparkSuccessor(node);
}
node.next = node; // help GC如果if的判斷能通過(guò),那就代表當(dāng)前這次嘗試是成功的,成功了就把鏈表都鏈上。
問(wèn)題來(lái)了,為什么這里不把Node.next(取消節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn))和Node(當(dāng)前取消節(jié)點(diǎn))的鏈給斷開(kāi),不斷開(kāi)的話(huà),JVM是無(wú)法回收掉Node(當(dāng)前取消節(jié)點(diǎn)),那不是內(nèi)存泄漏了?
Doug Lea這里是不是寫(xiě)的有問(wèn)題?
nonono.
當(dāng)正常喚醒節(jié)點(diǎn)時(shí),搶到鎖的節(jié)點(diǎn)會(huì)執(zhí)行
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}看到else條件下unparkSuccessor(node);的執(zhí)行邏輯。
這里已經(jīng)相當(dāng)難描述清楚,筆者會(huì)執(zhí)行流程和代碼都已經(jīng)寫(xiě)明白。
這里其實(shí)就是一個(gè)喚醒操作,喚醒的意義在于去執(zhí)行shouldParkAfterFailedAcquire()方法從后往前遍歷找到一個(gè)waitStatus不為1的節(jié)點(diǎn),然后鏈起來(lái)。
cancelAcquire
|->unparkSuccessor(node當(dāng)前取消節(jié)點(diǎn))
|->LockSupport.unpark(node.next.thread)
|->shouldParkAfterFailedAcquire(node(取消的節(jié)點(diǎn)),node.next(取消的節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)))// Node pred 是取消的節(jié)點(diǎn)
// Node node 是取消的節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
// 遍歷找到一個(gè)非取消的節(jié)點(diǎn)
// 并且把當(dāng)前取消的節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)與找到的非取消節(jié)點(diǎn)雙向鏈起來(lái)。
if (ws > 0) {
do {
// 往前走
node.prev = pred = pred.prev; // 鏈起來(lái)
} while (pred.waitStatus > 0); // 循環(huán)條件,所以找到一個(gè)小于等于0的節(jié)點(diǎn)就會(huì)退出
pred.next = node; // 鏈起來(lái)
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}最終喚醒節(jié)點(diǎn),走到shouldParkAfterFailedAcquire()方法中。從后往前的遍歷找到正常的節(jié)點(diǎn)
到此這篇關(guān)于AQS核心流程解析cancelAcquire方法的文章就介紹到這了,更多相關(guān)AQS cancelAcquire內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Data?JPA框架的核心概念與Repository接口詳解
Spring?Data?JPA是Spring基于JPA規(guī)范的基礎(chǔ)上封裝的?套?JPA?應(yīng)?框架,可使開(kāi)發(fā)者?極簡(jiǎn)的代碼即可實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的訪(fǎng)問(wèn)和操作,本篇我們來(lái)了解Spring?Data?JPA框架的核心概念與Repository接口2022-04-04
SpringMVC之簡(jiǎn)單的增刪改查示例(SSM整合)
本篇文章主要介紹了SpringMVC之簡(jiǎn)單的增刪改查示例(SSM整合),這個(gè)例子是基于SpringMVC+Spring+Mybatis實(shí)現(xiàn)的。有興趣的可以了解一下。2017-03-03
Java Scala偏函數(shù)與偏應(yīng)用函數(shù)超詳細(xì)講解
Scala是一種多范式的編程語(yǔ)言,支持面向?qū)ο蠛秃瘮?shù)式編程。Scala也支持異常處理,即在程序運(yùn)行過(guò)程中發(fā)生意外或錯(cuò)誤時(shí),采取相應(yīng)的措施2023-04-04
springboot2.3 整合mybatis-plus 高級(jí)功能(圖文詳解)
這篇文章主要介紹了springboot2.3 整合mybatis-plus 高級(jí)功能,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
Java中類(lèi)轉(zhuǎn)json的基類(lèi)實(shí)現(xiàn)
這篇文章主要介紹了Java中類(lèi)轉(zhuǎn)json的基類(lèi)實(shí)現(xiàn),需要的朋友可以參考下2021-01-01

