AQS源码分析

AQS源码分析,看这篇博客就够了,绝对的详细流弊

https://www.cnblogs.com/waterystone/p/4920797.html

但是我读了好几遍,这个方法还是不太理解,得注重再讲解一下:

private void doReleaseShared() {
    for (;;) {
        Node h = head;   拿到头节点,已经是刚唤醒的节点了
        if (h != null && h != tail) {  如果为空或者是队尾则不在唤醒
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {    //第一次进来,必定是这个状态,因为我们知道shouldParkAfterFailedAcquire必定是这个状态才能休息
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))  //将状态设置成0,第二次循环过来,好走else if这个很关键
                    continue;
                unparkSuccessor(h);//唤醒后继
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))  第一次唤醒后继节点了,如果head的节点没变,说明还在阻塞,修改状态退出
                continue;
        }
        if (h == head)// head发生变化  ,发生变化则直接退出
            break;
    }
}
大概流程:
调用这个方法有两个地方:
1:tryReleaseShared()方法,释放资源的时候
2:doAcquireShard()方法中,释放资源后,set头节点也会进行调用
第一步:先将状态设置成0,然后唤醒下一个节点,这个时候,下一个节点如果走的比较快,那么一定会重新设置了head节点,则直接退出

第二步:如果唤醒的后继节点还没走到,则for循环第二次,会将节点的状态设置成
PROPAGATE,然后就一直循环判断头节点有没有变化,退出即可。

方法:shouldParkAfterFailedAcquire

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;//拿到前驱的状态
    if (ws == Node.SIGNAL)
        //如果已经告诉前驱拿完号后通知自己一下,那就可以安心休息了
        return true;
    if (ws > 0) {
        /*
         * 如果前驱放弃了,那就一直往前找,直到找到最近一个正常等待的状态,并排在它的后边。
         * 注意:那些放弃的结点,由于被自己“加塞”到它们前边,它们相当于形成一个无引用链,稍后就会被保安大叔赶走了(GC回收)!
         */
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
         //如果前驱正常,那就把前驱的状态设置成SIGNAL,告诉它拿完号后通知自己一下。有可能失败,人家说不定刚刚释放完呢!
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

 

posted @ 2022-04-14 14:29  xzlnuli  阅读(21)  评论(0编辑  收藏  举报