JAVA-Lock解析-三-AbstractQueuedSynchronizer源码解析

本文是基于 JDK8 的源码分析

AbstractQueuedSynchronizer(以下简称AQS) 是实现 Lock 的基础,所有的Lock,Semaphore(信号量),StampedLock,CountDownLatch等都依赖 AQS。还有阻塞队列也是间接的依赖于它(阻塞队列直接依赖 Lock)。

AQS的实现主要依赖于自旋,LockSupport(park,unpark),CAS。

AQS 类图

AQS 有一个重要的成员变量 state,在独占锁和共享锁中,它表示的意思不同。

锁状态 描述
锁空闲 0
独占锁锁定,非重入锁 1
独占锁锁定,重入锁, > 0 锁重入次数
共享锁,非重入锁 指定值 n 表示同一时间可以有 n 个线程获取锁
共享锁,重入锁 指定值 共享锁计数
读写锁 高16位表示读锁,低16位表示写锁

AQS 有两个静态内部类 Node,ConditionObject

说明
Node 存放线程类的一个类,它的结构是一个链表。同步队列是是双向队列,条件队列时是单项队列。
ConditionObject 是 Condition 的一个实现类,用于线程等待和唤醒,功能类似 Object.wait 和 notify

ConditionObject 前一章已经讲过,这里分析下 Node:

    static final class Node {
        /** 指示节点正在共享模式下等待的标记 */
        static final Node SHARED = new Node();
        /** 指示节点正在以独占模式等待的标记 */
        static final Node EXCLUSIVE = null;

        /** waitStatus值,指示线程已取消 */
        static final int CANCELLED =  1;
        /** waitStatus值,指示后继线程需要释放,也表示当前线程处于阻塞或已经获取锁 */
        static final int SIGNAL    = -1;
        /** waitStatus值,指示线程正在等待队列种 */
        static final int CONDITION = -2;
        /**
         *waitStatus值,指示下一个acquireShared应该无条件传播
         * 当 Node 是 head 时,PROPAGATE 和0表示同一个意思
         */
        static final int PROPAGATE = -3;

        volatile int waitStatus;

        //前继节点,当 next 为 null 时,可以从尾部向前寻找节点
        volatile Node prev;
        //后继节点,如果 next != null 说明该节点已经在同步队列中
        volatile Node next;

        //当前线程,由构造方法初始化
        volatile Thread thread;
        
        //在同步队列中 nextWaiter 表示锁是共享模式还是独占模式,
        //在条件等待队列种时表示条件队列的下一个 Node,
        //条件队列是单向队列,同步队列是双向队列
        Node nextWaiter;

        //判断是否是共享锁模式还是独占锁模式
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        //返回前继节点
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }
        
        //用于建立初始标头或SHARED标记,如果用作头可以认为是一个dummy node
        Node() {    // Used to establish initial head or SHARED marker
        }
        
        //同步队列的构造方法
        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }
        
        //使用条件队列时的构造方法
        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

独占锁方法

1. acquire

独占模式下获取锁,忽略中断(会留下中断信号)。至少调用一次实现类的 tryAcquire 方法,如果失败则入队,这期间可能会有多次阻塞和非阻塞。

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();//如果有中断,则自我重新添加中断信号
}

tryAcquire 方法有类图中实现类实现。等到分析具体的锁时会具体分析,这里我们只要先知道独占锁获取锁时,它会调用 tryAcquire 一次,根据返回值会有不同的操作:

  1. 情况1:true,成功获取锁,立即返回
  2. 情况2:false,获取锁失败,入队,可能会阻塞,等待被唤醒

趁热打铁,看下情况2发生时是如果入队的。

1.1. 入队
    private Node addWaiter(Node mode) {//这时传入的模式为独占模式 Node.EXCLUSIVE
        Node node = new Node(Thread.currentThread(), mode);//以当前线程和传入的模式创建 Node
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {//末尾 Node != null,此时不需要创建dummy node,所以直接通过 CAS 拼接到末尾,如果 CAS 失败,进入 enq 自旋,直到入队
            //以下操作的目的就是把当前 Node 接到末尾
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {//CAS 设置末尾 Node,因为此时可能会有多个竞争失败的 Node,失败则进入 enq
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

    private Node enq(final Node node) {
        for (;;) {//自旋,知道入队成功
            Node t = tail;
            if (t == null) { // Must initialize 初始化,是否是第一次出现竞争,如果是则初始化 head 和 tail
                if (compareAndSetHead(new Node()))//CAS 设置头为一个 dummy node (虚假 node),方便后面的判断,竞争失败的线程自旋
                    tail = head;
            } else {//tail != null,正常入队
                //和 addWaiter 中一样的操作,把当前 Node 接到末尾
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

至此,封装且入队结束,接下来就是进入多次的阻塞和非阻塞。

1.2. 多次阻塞和非阻塞,获取锁
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;//用于判断是否被中断
            for (;;) {
                final Node p = node.predecessor();//获取前继 Node
                //如果前一个 Node 是头 Node,则调用 tryAcquire 尝试获取锁
                if (p == head && tryAcquire(arg)) {
                    //获取成功,把当前 Node 设置为头 Node
                    setHead(node);
                    p.next = null; // help GC 去除引用
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) && //判断是否应该阻塞
                    parkAndCheckInterrupt())//如果 shouldParkAfterFailedAcquire(p, node)  == true 则阻塞线程
                    //如果因为被中断而唤醒线程则把 interrupted 设置为 true
                    interrupted = true;
            }
        } finally {
            if (failed)//由于某些原因导致失败,则取消 Node
                cancelAcquire(node);
        }
    }
1.2.1. shouldParkAfterFailedAcquire

这个方法的作用就是把前一个节点的设置为 SIGNAL

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            //如果前一个 Node 的 ws 已经是 SIGNAL 说明前一个节点已设置过且正常
            return true;
        if (ws > 0) { 
            //>0 说明已取消,出现这种情况的原因有可能是,上一个尾部 Node 出现异常且在cas 操作时失败
            //do while 的作用是循环的往前找没有被取消的 Node,并把当前 Node 连接到它后面
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            //前一个 Node 的 ws 是0 或 PROPAGATE,把 ws 设置为 Node.SIGNAL,但不会阻塞,只会重试一次获取锁,如果失败才会进入阻塞
            //这里可能 pred 正在走 cancelAcquire 方法,也就是正在被取消
            //也可能 pred 是 head 正在释放锁并修改ws
            //所以 compareAndSetWaitStatus(pred, ws, Node.SIGNAL) 可能会失败(失败后,ws == 0 || ws == 1),但是返回 false 会重试。重试时如果是 0 则会循环的竞争锁。如果是 -1 进入 if (ws > 0)
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

这里搞清楚 waitStatus 各个值的意思,已在 Node 中介绍。可以这样理解,只要 pred 的 ws 不是 SIGNAL 都会无限循环,直到队列稳定。

1.2.2. parkAndCheckInterrupt
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);//阻塞,等待唤醒
    return Thread.interrupted();//获取并返回中断信号,并重置中断信号
}
1.2.3. cancelAcquire

把获取锁异常的线程取消,cancelAcquire 发生竞争只有在入队 Node 和 头 No

private void cancelAcquire(Node node) {
    // Ignore if node doesn't exist
    if (node == null)
        return;
    node.thread = null;
    //找到当前 Node 之前的未取消的 Node,并把它设置为当前 Node 的前继 Node
    Node pred = node.prev;
    while (pred.waitStatus > 0)
        node.prev = pred = pred.prev;
    // predNext 是很明显的不拼接的 Node
    Node predNext = pred.next;
    //这里没有使用 CAS 因为在 shouldParkAfterFailedAcquire 方法中已经说过会主动跳过已取消的 Node。
    //我觉得这一步放到 Node pred = node.prev; 会不会更好一点呢?
    node.waitStatus = Node.CANCELLED;
    //如果当前 Node 是末尾,说明 pred 后面的 Node 都已经被取消,则直接把 pred 置为末尾, predNext 置为 null
    //这里使用 CAS 因为 cancelAcquire 方法没有使用同步锁,导致可能有多个异常尾部 Node 同时执行到这一步
    if (node == tail && compareAndSetTail(node, pred)) {
        compareAndSetNext(pred, predNext, null);
    } else {
        //不是尾部 Node 的情况
        // If successor needs signal, try to set pred's next-link
        // so it will get one. Otherwise wake it up to propagate.
        int ws;
        //pred 不是头,
        //pred 的 ws == SIGNAL || ws <= 0 并且设置 SIGNAL 成功
        //pred 的 thread != null
        //当前 Node 的后继 !=null && ws <= 0
        //满足以上所有条件后,把当前 Node 的后继拼接到 pred 的后面
        //需要注意:可能有多个线程同时到这一步,
        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)
                //这里没有 next.pred = pred,因为 shouldParkAfterFailedAcquire 方法可以修复
                //这一步可能把一个待取消 Node 设置成功,因为可能 pred 正好运行到 node.thread = null; 之前的某一步。
                //但都OK,因为有 shouldParkAfterFailedAcquire 做保证
                compareAndSetNext(pred, predNext, next);
        } else {
            //1. pred 是头
            //2. pred 不是头 && predWs == 已取消
            //3. pred 不是头 && predWs <= 0 && CAS 修改 predWs 失败
            //4. pred 不是头 && predWs == SIGNAL && pred.thread == null
            //5. pred 不是头 && predWs <= 0 && CAS 修改 predWs 成功 && pred.thread == null
            //以上5中情况都会进入else唤醒后继。唤醒的作用是:
            //如果 pred 是头,则尝试获取锁,
            //如果不是头,则调用 shouldParkAfterFailedAcquire 方法把已取消的 Node 过滤
            unparkSuccessor(node);
        }
        node.next = node; // help GC
    }
}

该方法并发取消时 Node时,情况比较复杂,但只要配合 shouldParkAfterFailedAcquire 都可以修复。需要仔细思考才能弄明白,但始终要记住,无论怎么并发,它无论怎么并发取消,最后的结果都是去除已取消 Node。

  1. 情况1:头后面的 Node 被取消,此时配合 unparkSuccessor,此时从尾部开始开始往前找,找到最后一个没有取消的 Node,并唤醒。
1.2.4. unparkSuccessor

唤醒传入 Node 的后继 Node

private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    if (ws < 0)//这里使用符号判断,因为在共享锁时,有可能 ws 可能被改为 PROPAGATE,请看 8.1 doReleaseShared 方法
        compareAndSetWaitStatus(node, ws, 0);
    
    //获取下一个 Node s,如果 s == null || s 已取消,则从尾部往前找,遍历 tail - node 区间,找到没有被取消的的第一个 Node,
    //配合者唤醒和 shouldParkAfterFailedAcquire 方法,该 Node 应该就是头的下一个 Node,也就能获取锁了。
    //(Node 的 pred 变量是 head 才有资格获取锁)。完美
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {//s.waitStatus > 0 的情况是 head 后至少有三个连续线程发生异常或者连续四个 Node 发生异常
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        //唤醒,s.thread == null 也没事,这就让当有多个连续线程取消时,不会出现问题,也许 s 正在走 cancelAcquire。
        LockSupport.unpark(s.thread);
}

2. acquireInterruptibly

可中断获取独占锁,该方法如果检测到线程被中断则抛出 InterruptedException。对应 Lock 接口中的 lockInterruptibly 方法。

public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (!tryAcquire(arg))//和 acquire 首先尝试获取锁
        doAcquireInterruptibly(arg);//失败后
}
2.1. doAcquireInterruptibly
private void doAcquireInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.EXCLUSIVE);//入队,请看1.1节,Node.EXCLUSIVE,可以直到是独占锁
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

可以看到和 acquireQueued 方法(1.2 节)几乎一模一样,只是返回值不一样,acquireQueued 返回是否中断的布尔值,doAcquireInterruptibly 方法无返回值,发生中断时在第 16 行抛出异常。

3. tryAcquireNanos

在给定时间内可中断的获取锁,如果给定时间内获取成功则失败。对应 Lock 接口中的 tryLock(long, TimeUnit) 方法。

public final boolean tryAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquire(arg) ||//首先尝试调用 tryAcquire 方法,如果返回 false 则进入 doAcquireNanos
        doAcquireNanos(arg, nanosTimeout);
}
3.1. doAcquireNanos
private boolean doAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (nanosTimeout <= 0L)
        return false;
    final long deadline = System.nanoTime() + nanosTimeout;//deadline 表示超时时间点
    final Node node = addWaiter(Node.EXCLUSIVE);//入队,请查看1.1节
    boolean failed = true;//用于判断是否需要取消
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return true;
            }
            nanosTimeout = deadline - System.nanoTime();//重新计算 nanosTimeout 超时时间
            if (nanosTimeout <= 0L)//指定时间内获取锁,退出循环
                return false;
            if (shouldParkAfterFailedAcquire(p, node) &&//请看1.2.1节
                nanosTimeout > spinForTimeoutThreshold)//超时时间是否 > 1ms,这里如果超时时间 < 1ms,线程会在1ms内循环,直到超时或获取锁或中断。
                LockSupport.parkNanos(this, nanosTimeout);//这里有点不一样,它使线程阻塞指定时间
            if (Thread.interrupted())//如果被中断则抛出异常
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

9-15 和 acquireQueueddoAcquireInterruptibly一样。这里唯一不一样的就是阻塞的方法,第22行使用了LockSupport.parkNanos(this, nanosTimeout) 而不是 parkAndCheckInterrupt 中的 LockSupport.park(this) 方法。

到这里所有的独占锁的获取讲完了,有获取就有释放 release

4. release

独占锁释放,首先会调用实现类的 tryRelease 方法,返回 true 才会取唤醒下一个 Node。对应 Lock 接口中的 unlock 方法。

public final boolean release(int arg) {
    if (tryRelease(arg)) {//调用实现类,返回 false 说明有锁重入
        Node h = head;
        if (h != null && h.waitStatus != 0)//h.waitStatus == SIGNAL 才能唤醒
            unparkSuccessor(h);//唤醒,请看第1.2.4节
        return true;
    }
    return false;
}

共享锁方法

共享锁方法和独占锁方法大体上相差不大,方法也是共享的,所以共享锁讲的可能不会很详细。

5. acquireShared

共享锁获取,忽略中断信号,首先调用 tryAcquireShared 方法,如果返回值小于0(说明共享锁没有了),则入队。

public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}
5.1. doAcquireShared

写法和独占锁的 acquireQueued 方法一样。

private void doAcquireShared(int arg) {
    final Node node = addWaiter(Node.SHARED);//入队,请看第1.1节。可以看到共享锁使用的是Node.SHARED,这和 Node 分析是讲的一样。
    boolean failed = true;//是否异常
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();//获取前继 Nodo
            if (p == head) {//判断 p 是否是头
                int r = tryAcquireShared(arg);//尝试获取共享锁
                if (r >= 0) {//>=0,说明获取成功
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();//自我中断
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&//请看第1.2.1节
                parkAndCheckInterrupt())//阻塞,请看第1.2.2节
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);//取消异常 Node
    }
}

其中和独占锁最重要的区别就是 setHeadAndPropagate,这也是共享锁的关键 。这使得多个线程同时获取锁,不需要等到锁释放时才能获取锁。

5.2. setHeadAndPropagate
private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; // 记录head,用于后面检查
    //把 node 设置为头
    //这里不需要加锁操作,因为获取共享锁后,会从FIFO队列中依次唤醒队列,并不会产生并发安全问题
    setHead(node);
    // 唤醒队列中的下一个 Node,需要满足以下两点:
    // 1. 传播是由调用方指示的(即,propagate > 0),或者是由上一个操作记录的(作为setHead之前或之后的h.waitStatus)
    //(请注意:这使用waitStatus的符号检查(即,h.waitStatus < 0),因为PROPAGATE状态可能会转换为SIGNAL。)
    // 2. 下一个节点正在共享模式下等待,或我们不知道,因为它显示为空

    // 1. propagate > 0, 说明还有共享锁可以被获取
    // 2. h == null,h.waitStatus < 0
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {//获取 setHead 后的 Node
        Node s = node.next;
        // 这里可以理解为除非明确指明不需要唤醒(后继等待节点是独占锁模式),否则都要唤醒
        if (s == null || s.isShared())
            doReleaseShared();
    }
}

这里有个地方比较难理解,为什么对 h 判断了两次?
我的理解是:首先要明白 propagate < 0 而且在队列中,说明当前共享锁已经被占用完了。 此时判断 h.waitStatus < 0 小于0,说明锁还没释放,唤醒下一个 Node,下一个 Node就可能获取锁,当 h.waitStatus >= 0时,说明锁已经释放,已经没有空闲的共享锁可以获取了,只能等待在锁释放。这里判断两次都是这个道理。只要setHead 前后的 Node 有一个没有释放锁,下一个 Node 都有可能获取锁。这样看来,其实我觉得这里不判断 h 的状态也可以,只是获取锁的几率会低一点。都是出于性能的考虑吧。

6. acquireSharedInterruptibly

中断获取共享锁。对应独占锁的 acquireInterruptibly

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)//首先调用 实现类的 tryAcquireShared,如果返回值 < 0,则入队
        doAcquireSharedInterruptibly(arg);
}
6.1. doAcquireSharedInterruptibly

此方法和 doAcquireShared 几乎一样,只是在第19的处理上不一样,此方法会抛出异常。对应独占锁 doAcquireInterruptibly 方法。

private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);//请看第5.2节
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

7. tryAcquireSharedNanos

在指定时间内中断获取共享锁。对应独占锁的 tryAcquireNanos 方法

public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquireShared(arg) >= 0 ||
        doAcquireSharedNanos(arg, nanosTimeout);
}
7.1. doAcquireSharedNanos

对应 doAcquireNanos 方法

private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (nanosTimeout <= 0L)
        return false;
    final long deadline = System.nanoTime() + nanosTimeout;
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);//请看第5.2节
                    p.next = null; // help GC
                    failed = false;
                    return true;
                }
            }
            nanosTimeout = deadline - System.nanoTime();
            if (nanosTimeout <= 0L)
                return false;
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

释放独占锁

8. releaseShared

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {//如果释放成功,则调用 doReleaseShared 方法,和独占锁有点区别,也是非常重要的区别
        doReleaseShared();
        return true;
    }
    return false;
}

这个方法也是唯一没有和独占锁有大多相似的方法。

8.1 doReleaseShared

该方法和独占锁有非常大的区别,共享锁会传播,因为和独占锁不一样,共享锁可能有多个线程同时释放锁,这时候如果还是像独占锁一样,一个一个的释放,显然效率会很低,所以这里采用了不同的释放锁方式。也说明了共享锁是需要传播的。

该方法有两个调用入口:setHeadAndPropagate 和 releaseShared

private void doReleaseShared() {
    //即使有其他线程正在获取/释放锁,也要确保释放传播。
    //如果有唤醒信号,则此方法会正常唤醒后继 Node (即,调用 unparkSuccessor 方法)。
    //如果没有唤醒信号,WaitStatus 会被置为 PROPAGATE
    //(在讲 shouldParkAfterFailedAcquire 时讲到,当 WaitStatus = 0 || PROPAGATE 时,线程不会阻塞,而是会重试获取锁)
    for (;;) {
        Node h = head;//这里 head 可能一直在变
        if (h != null && h != tail) {//如果  h == tail 说明已经没有等待线程了
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    // 这里有可能有两种情况:
                    //1. continue 之后还是原来的 head,则进入到 else
                    //2. continue 之后 head 已经改变,则再次 compareAndSetWaitStatus(h, Node.SIGNAL, 0)
                    continue;            // loop to recheck cases,
                unparkSuccessor(h);
            }
            // 1. 拿到和 unparkSuccessor 线程相同的 head 但是 ws == Node.SIGNAL 时 ws 已经被其他线程修改。
            // 2. compareAndSetWaitStatus 竞争失败且 head 没变
            // 3. compareAndSetWaitStatus 竞争失败且 head 没变 compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) 失败,
            //这一步说明,就算 unparkSuccessor 中把 head 的 ws 改为 0 之后还是可以把 head ws 改为 PROPAGATE,这也在 shouldParkAfterFailedAcquire 中体现出来了
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}

总结

到这里 AQS 的重要方法都讲完了,其他的都是比较简单的辅助方法。可以看到 AQS 中独占锁和共享锁方法基本上是一一对应的。这里主要难理解的有 cancelAcquireshouldParkAfterFailedAcquiresetHeadAndPropagatedoReleaseShared 四个方法和相应 waitStatus 的变化。如果有不对的地方请大家纠正。

你可能感兴趣的:(JAVA-Lock解析-三-AbstractQueuedSynchronizer源码解析)