AQS源码深度解析之cancelAcquire方法解读

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指针开始从后往前,找到最靠近头的有效(非取消)节点,唤醒这个线程。

完美!

posted @ 2022-10-08 15:04  李东平|一线码农  阅读(300)  评论(0编辑  收藏  举报