AQS-doAcquireShared源码解读

acquireShared方法,先会执行tryAcquireShared方法,而核心的代码都在doAcquireShared方法中

源码如下:

private void doAcquireShared(int arg) {
        //加入尾部队列
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

逻辑:先会把当前节点加到同步队列的尾部,然后进入自旋,自旋的作用是:获取锁,或者陷入阻塞.
如果当前节点的前驱节点是head,当前线程就会去获取锁,
如果获取失败,就会执行shouldParkAfterFailedAcquire,同时将前驱节点的状态改成SIGNAL.
如果获取成功,就会执行setHeadAndPropagate

setHeadAndPropagate的源码如下:

private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
        /*
         * propagate>0,说明还有资源
         * h.waitStatus < 0 h的waitStatus要么是-1,要么是-3,
         * 满足以上2个条件之一,就能进入doReleaseShared(),释放后继节点了
         */
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

假如说第一个线程进来,执行setHeadAndPropagate时waitStatus还是初始状态0,只要满足propagate > 0条件节能进入doReleaseShared方法
h == null的场景暂时未知
如果进来的线程的waitStatus为-1或者-3时,也可以进入doReleaseShared()
那么这个线程waitStatus什么时候被改成-1或者-3的呢?
答案是后继节点做的:
1 当线程进来时候,发现前面节点不是head的时候,那么会执行shouldParkAfterFailedAcquire,把前面节点状态设置为-1,那么当前面的节点执行到setHeadAndPropagate的时候,就能进入doReleaseShared()

看看doReleaseShared()源码:

private void doReleaseShared() {
        /*
         *
         *会先判断head节点的waitStatus,doReleaseShared只被2个方法调用,1个是setHeadAndPropagate,1个是releaseShared,我只讨论setHeadAndPropagate方法调用的情况
         *所以进来的head的waitStatus也只有3种可能,0,-1,-3.
         *如果是0的情况说明还有资源,但是后继节点为空,则会把waitStatus设置成-3,便结束这个方法.当节点释放锁的时候,会执行releaseShared,如果这个时候waitStatus还是0,说明后面还是没节点,如果后面有节点,则必定会更改waitStatus为-1
         *如果是-1,则会更改成0并且唤醒后继节点
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

你可能感兴趣的:(AQS-doAcquireShared源码解读)