最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

AQS核心流程解析cancelAcquire方法

 更新時間:2023年04月10日 09:48:29   作者:程序員李哈  
可以清楚的看到在互斥鎖和共享鎖的拿鎖過程中都是有調(diào)用此方法的,而cancelAcquire()方法是寫在finally代碼塊中,并且使用failed標(biāo)志位來控制cancelAcquire()方法的執(zhí)行

引出問題

首先,先考慮一個問題,什么條件會觸發(fā)cancelAcquire()方法?

cancelAcquire()方法的反向查找

可以清楚的看到在互斥鎖和共享鎖的拿鎖過程中都是有調(diào)用此方法的,而cancelAcquire()方法是寫在finally代碼塊中,并且使用failed標(biāo)志位來控制cancelAcquire()方法的執(zhí)行??梢缘贸?,在觸發(fā)異常的情況下會執(zhí)行cancelAcquire()方法。

響應(yīng)中斷的獲鎖方法

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

private void cancelAcquire(Node node) {
    // Ignore if node doesn't exist
    if (node == null)
        return;
    // 當(dāng)前節(jié)點(diǎn)的線程指向置為null
    node.thread = null;
    // 協(xié)同取消的處理。
    // 這里是判斷當(dāng)前節(jié)點(diǎn)的上一個節(jié)點(diǎn)的狀態(tài)是否是取消狀態(tài)(狀態(tài)大于0只有是取消狀態(tài))
    // 如果上一個節(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)的下一個節(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),也就是最后一個節(jié)點(diǎn)
    // 那么就把tail指針指向上面while循環(huán)遍歷出的prev節(jié)點(diǎn)(因?yàn)橐赶蛞粋€沒有被取消的節(jié)點(diǎn))。
    if (node == tail && compareAndSetTail(node, pred)) {
        // help GC
        // 為什么說help GC呢?
        // 因?yàn)榘裵rev的next節(jié)點(diǎn)設(shè)置為null,
        // 這樣GC ROOT掃描發(fā)現(xiàn)沒有根節(jié)點(diǎn)的引用。
        compareAndSetNext(pred, predNext, null);
    } else {
        // 走到else代表當(dāng)前節(jié)點(diǎn)不是tail節(jié)點(diǎn),或者是cas操作的時候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)的線程指向不為null(如果為null代表在取消的過程中)
        // 因?yàn)?amp;&是拼接,所以上面任意一個條件為false就會進(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)的下一個節(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)的下一個節(jié)點(diǎn)。
            // 喚醒的目的是為了去執(zhí)行shouldParkAfterFailedAcquire方法去處理取消節(jié)點(diǎn)。
            unparkSuccessor(node);
        }
        // 把當(dāng)前節(jié)點(diǎn)的下一個節(jié)點(diǎn)指向自己.
        // help gc。
        node.next = node; // help GC
    }
}

比較復(fù)雜,所以筆者為了讀者的觀看順利, 下面會拆分步驟,并且畫圖來理解。

跳過已經(jīng)取消的節(jié)點(diǎn),找到一個非取消的節(jié)點(diǎn)

// Skip cancelled predecessors
// 跳過已經(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)的下一個節(jié)點(diǎn)也是正常的情況(非取消)
    if (next != null && next.waitStatus <= 0)
        compareAndSetNext(pred, predNext, next);
} else {
    // 嘗試失敗,只能走比較狠的邏輯去處理了。
    unparkSuccessor(node);
}
node.next = node; // help GC

如果if的判斷能通過,那就代表當(dāng)前這次嘗試是成功的,成功了就把鏈表都鏈上。

問題來了,為什么這里不把Node.next(取消節(jié)點(diǎn)的下一個節(jié)點(diǎn))和Node(當(dāng)前取消節(jié)點(diǎn))的鏈給斷開,不斷開的話,JVM是無法回收掉Node(當(dāng)前取消節(jié)點(diǎn)),那不是內(nèi)存泄漏了?

Doug Lea這里是不是寫的有問題?

nonono.

當(dāng)正常喚醒節(jié)點(diǎn)時,搶到鎖的節(jié)點(diǎn)會執(zhí)行

private void setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}

看到else條件下unparkSuccessor(node);的執(zhí)行邏輯。

這里已經(jīng)相當(dāng)難描述清楚,筆者會執(zhí)行流程和代碼都已經(jīng)寫明白。

這里其實(shí)就是一個喚醒操作,喚醒的意義在于去執(zhí)行shouldParkAfterFailedAcquire()方法從后往前遍歷找到一個waitStatus不為1的節(jié)點(diǎn),然后鏈起來。

cancelAcquire
    |->unparkSuccessor(node當(dāng)前取消節(jié)點(diǎn))
        |->LockSupport.unpark(node.next.thread)
            |->shouldParkAfterFailedAcquire(node(取消的節(jié)點(diǎn)),node.next(取消的節(jié)點(diǎn)的下一個節(jié)點(diǎn)))
// Node pred 是取消的節(jié)點(diǎn)
// Node node 是取消的節(jié)點(diǎn)的下一個節(jié)點(diǎn)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    // 
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        return true;
    // 遍歷找到一個非取消的節(jié)點(diǎn)
    // 并且把當(dāng)前取消的節(jié)點(diǎn)的下一個節(jié)點(diǎn)與找到的非取消節(jié)點(diǎn)雙向鏈起來。
    if (ws > 0) {
        do {
            // 往前走
            node.prev = pred = pred.prev;     // 鏈起來
        } while (pred.waitStatus > 0);        // 循環(huán)條件,所以找到一個小于等于0的節(jié)點(diǎn)就會退出
        pred.next = node;              // 鏈起來
    } 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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺析JVM如何處理Java中的精度轉(zhuǎn)換

    淺析JVM如何處理Java中的精度轉(zhuǎn)換

    在Java編程中,理解不同數(shù)據(jù)類型之間的轉(zhuǎn)換機(jī)制對于寫出高效、正確的代碼至關(guān)重要,本文將詳細(xì)探討Java中的精度轉(zhuǎn)換機(jī)制,有需要的小伙伴可以了解下
    2025-03-03
  • SpringBoot整合MyBatis實(shí)現(xiàn)CRUD操作項(xiàng)目實(shí)踐

    SpringBoot整合MyBatis實(shí)現(xiàn)CRUD操作項(xiàng)目實(shí)踐

    本文主要介紹了SpringBoot整合MyBatis實(shí)現(xiàn)CRUD操作項(xiàng)目實(shí)踐,如何實(shí)現(xiàn)數(shù)據(jù)庫的CRUD創(chuàng)建、讀取、更新、刪除操作,具有一定的參考價值,感興趣的可以了解一下
    2024-02-02
  • Java類的訪問權(quán)限關(guān)鍵字用法說明

    Java類的訪問權(quán)限關(guān)鍵字用法說明

    這篇文章主要介紹了Java類的訪問權(quán)限關(guān)鍵字用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • SpringBoot中的multipartResolver上傳文件配置

    SpringBoot中的multipartResolver上傳文件配置

    這篇文章主要介紹了SpringBoot中的multipartResolver上傳文件配置,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 了解java中的session

    了解java中的session

    這篇文章主要介紹了了解java中的session的相關(guān)問題,什么是session,session怎么用等,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • 你要知道IDEA的這些必備插件

    你要知道IDEA的這些必備插件

    這篇文章主要介紹了你要知道IDEA的這些必備插件,文中有非常詳細(xì)的圖文示例及代碼,對正在使用IDEA的小伙伴們有很好的幫助喲,需要的朋友可以參考下
    2021-05-05
  • Java干貨知識深入理解內(nèi)部類

    Java干貨知識深入理解內(nèi)部類

    這篇文章主要介紹了Java干貨知識深入理解內(nèi)部類,定義在另一個類或方法中的類就叫做內(nèi)部類,需要的朋友可以參考下
    2019-06-06
  • spring 和 spring boot 中的屬性配置方式

    spring 和 spring boot 中的屬性配置方式

    這篇文章主要介紹了spring 和 spring boot 中的屬性配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Springboot?@Async多線程獲取返回值方式

    Springboot?@Async多線程獲取返回值方式

    這篇文章主要介紹了Springboot?@Async多線程獲取返回值方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Java單例模式的深入了解

    Java單例模式的深入了解

    這篇文章主要為大家介紹了Java單例模式,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01

最新評論

义马市| 论坛| 吴川市| 翁牛特旗| 万安县| 永春县| 治县。| 巢湖市| 咸丰县| 新兴县| 当涂县| 上虞市| 珠海市| 虎林市| 大埔区| 莱西市| 孙吴县| 宜阳县| 内乡县| 宁远县| 阿尔山市| 枝江市| 江安县| 乐东| 渭源县| 马关县| 昌宁县| 江川县| 东港市| 青冈县| 岫岩| 宜兰市| 哈巴河县| 金湖县| 周至县| 精河县| 永州市| 长治市| 昌邑市| 台东市| 乳源|