本文是基于 JDK8 的源码分析
AbstractQueuedSynchronizer(以下简称AQS) 是实现 Lock 的基础,所有的Lock,Semaphore(信号量),StampedLock,CountDownLatch等都依赖 AQS。还有阻塞队列也是间接的依赖于它(阻塞队列直接依赖 Lock)。
AQS的实现主要依赖于自旋,LockSupport(park,unpark),CAS。
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:true,成功获取锁,立即返回
- 情况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:头后面的 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 和 acquireQueued
,doAcquireInterruptibly
一样。这里唯一不一样的就是阻塞的方法,第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 中独占锁和共享锁方法基本上是一一对应的。这里主要难理解的有 cancelAcquire
,shouldParkAfterFailedAcquire
,setHeadAndPropagate
,doReleaseShared
四个方法和相应 waitStatus 的变化。如果有不对的地方请大家纠正。