乐于分享
好东西不私藏

AQS源码逐行精读:CLH队列、CAS、独占/共享模式的设计哲学

AQS源码逐行精读:CLH队列、CAS、独占/共享模式的设计哲学

一、AQS的整体架构

AQS(AbstractQueuedSynchronizer)是JUC包的基石,ReentrantLockCountDownLatchSemaphoreReentrantReadWriteLock都建在它上面。

核心结构:

┌─────────────────────────────────────────────┐│  AbstractQueuedSynchronizer                  ││                                               ││  ┌─────────────┐    ┌──────────────────────┐ ││  │ state (int) │◄───│ CLH Variant Queue    │ ││  │ volatile    │    │ (FIFO wait queue)    │ ││  └─────────────┘    │                      │ ││                      │  head ←→ node1 ←→   │ ││  ┌─────────────┐    │  node2 ←→ node3 ←→.. │ ││  │ exclusiveOwner│   └──────────────────────┘ ││  │ (Thread)    │                               ││  └─────────────┘                               │└─────────────────────────────────────────────┘

三个核心:

  1. volatile int state——同步状态,独占模式表示重入次数/锁占用,共享模式表示剩余许可数
  2. CLH变体队列——FIFO双向链表,线程阻塞/唤醒的载体
  3. CAS——所有状态变更的原子操作基础

二、Node:CLH队列的最小单元

staticfinalclassNode{// 共享模式staticfinal Node SHARED = new Node();// 独占模式staticfinal Node EXCLUSIVE = null;// 等待状态staticfinalint CANCELLED =  1;  // 超时/中断,出队staticfinalint SIGNAL    = -1;  // 后继需要被唤醒staticfinalint CONDITION = -2;  // 在Condition队列中staticfinalint PROPAGATE = -3;  // 共享模式传播volatileint waitStatus;  // 节点状态volatile Node prev;       // 前驱volatile Node next;       // 后继volatile Thread thread;   // 等待线程    Node nextWaiter; // Condition队列中的后继,或共享模式下的特殊链表}

设计要点:

  • waitStatus用volatile而非volatile int数组——每个Node独立可见,避免伪共享
  • prevnext用volatile保证多线程下链表操作的可见性
  • thread设为volatile:中断时需要快速找到目标线程
  • SHAREDEXCLUSIVE用Node实例和null区分,而非枚举——省内存、省比较

CLH队列与原版CLH锁的区别

原版CLH锁是自旋锁,没有waitStatus,只靠自旋。AQS改造:

原版CLH: 每个节点自旋等前驱释放AQS CLH: 节点挂起(park),靠waitStatus+park/unpark协作唤醒

关键点:

  • 增加waitStatus,避免无效唤醒
  • LockSupport.park()替代自旋,节省CPU
  • 支持超时取消(CANCELLED状态)

三、state的CAS操作:原子性的根基

AQS所有核心方法都依赖CAS修改state:

// Unsafe直接操作privatestaticfinal Unsafe unsafe = Unsafe.getUnsafe();privatestaticfinallong stateOffset;static {try {        stateOffset = unsafe.objectFieldOffset            (AbstractQueuedSynchronizer.class.getDeclaredField("state"));    } catch (Exception ex) { thrownew Error(ex); }}protectedfinalbooleancompareAndSetState(int expect, int update){return unsafe.compareAndSwapInt(this, stateOffset, expect, update);}

为什么用Unsafe而不是AtomicInteger:

  • AtomicInteger底层也是Unsafe.compareAndSwapInt
  • AQS需要更细粒度控制(加失败后的自旋重试逻辑)
  • 直接用Unsafe少一层封装,性能更可控

CAS的重试:acquireQueued中的自旋

// 独占模式获取锁的核心循环finalbooleanacquireQueued(final Node node, int arg){boolean failed = true;try {boolean interrupted = false;for (;;) {  // 自旋final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {  // 前驱是head且自己抢到了                setHead(node);                p.next = null;  // help GC                failed = false;return interrupted;            }if (shouldParkAfterFailedAcquire(p, node) &&                parkAndCheckInterrupt())                interrupted = true;        }    } finally {if (failed)            cancelAcquire(node);    }}

设计哲学:CAS失败不立刻park,先检查前驱状态。只有前驱是SIGNAL或CANCELLED时才park——减少无效park/unpark。


四、独占模式(Exclusive):ReentrantLock的底层

4.1 tryAcquire:获取锁的两次机会

// NonfairSync(非公平)finalbooleannonfairTryAcquire(int acquires){final Thread current = Thread.currentThread();int c = getState();if (c == 0) {  // 第一次机会:锁空闲,直接CASif (compareAndSetState(0, acquires)) {            setExclusiveOwnerThread(current);returntrue;        }    }elseif (current == getExclusiveOwnerThread()) {  // 第二次机会:重入int nextc = c + acquires;if (nextc < 0// overflowthrownew Error("Maximum lock count exceeded");        setState(nextc);returntrue;    }returnfalse;}// FairSync(公平)finalbooleantryAcquire(int acquires){final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (!hasQueuedPredecessors() &&  // 多了一个检查:队列有没有前驱            compareAndSetState(0, acquires)) {            setExclusiveOwnerThread(current);returntrue;        }    }elseif (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)thrownew Error("Maximum lock count exceeded");        setState(nextc);returntrue;    }returnfalse;}

公平与非公平的唯一区别:公平版在CAS之前加了hasQueuedPredecessors()检查。

staticfinalbooleanhasQueuedPredecessors(){    Node h = head;    Node t = tail;return h != t &&  // 队列非空           (h.next == null || h.thread != Thread.currentThread());// 注意:h.next == null 说明正在初始化,不算有前驱}

4.2 release:释放锁的传播

publicfinalbooleanrelease(int arg){if (tryRelease(arg)) {        Node h = head;if (h != null && h.waitStatus != 0)            unparkSuccessor(h);  // 唤醒后继returntrue;    }returnfalse;}protectedfinalbooleantryRelease(int releases){int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread())thrownew IllegalMonitorStateException();boolean free = (c == 0);if (free)        setExclusiveOwnerThread(null);    setState(c);return free;}

unparkSuccessor的传播逻辑:

privatevoidunparkSuccessor(Node node){int ws = node.waitStatus;if (ws < 0)        compareAndSetWaitStatus(node, ws, 0);  // 清除SIGNAL    Node s = node.next;if (s == null || s.waitStatus > 0) {   // s为null或已取消        s = null;for (Node t = tail; t != null && t != node; t = t.prev)if (t.waitStatus <= 0)                s = t;    }if (s != null)        LockSupport.unpark(s.thread);}

从tail向前扫描的设计:node.next可能已经无效(因为node被取消时next会被置为null),所以从tail倒序找到最近的非取消节点。这是AQS中为数不多的反向遍历。


五、共享模式(Shared):Semaphore/CountDownLatch的底层

5.1 tryAcquireShared:抢许可

// Semaphore的NonfairSyncprotectedinttryAcquireShared(int acquires){for (;;) {int available = getState();int remaining = available - acquires;if (remaining < 0 ||            compareAndSetState(available, remaining))return remaining;    }}

返回值含义:

  • >= 0:获取成功,返回剩余许可数
  • < 0:获取失败,返回负值(绝对值=需要等待的许可数)

5.2 doAcquireShared:入队+park

privatevoiddoAcquireShared(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;                    failed = false;return;                }            }if (shouldParkAfterFailedAcquire(p, node) &&                parkAndCheckInterrupt())                interrupted = true;        }    } finally {if (failed)            cancelAcquire(node);    }}

5.3 setHeadAndPropagate:共享模式的关键传播

这是共享模式与独占模式最大的不同:

privatevoidsetHeadAndPropagate(Node node, int propagate){    Node h = head;    setHead(node);if (propagate > 0 || h == null || h.waitStatus < 0 ||        (h = head) == null || h.waitStatus < 0) {        Node s = node.next;if (s == null || s.isShared())            doReleaseShared();  // 唤醒后继共享节点    }}

传播逻辑:

  • propagate > 0:还有剩余许可,可以唤醒后继
  • h.waitStatus < 0:原head是SIGNAL状态,需要传播
  • 唤醒时只唤醒isShared()的节点——不唤醒独占节点,避免浪费

六、队列操作:enq的入队细节

private Node enq(final Node node){for (;;) {        Node t = tail;if (t == null) {  // 初始化if (compareAndSetHead(new Node()))                tail = head;        } else {            node.prev = t;if (compareAndSetTail(t, node)) {                t.next = node;return t;  // 返回前驱,用于park前检查            }        }    }}

两次CAS的设计:

  1. 第一次CAS初始化head(只在tail为null时)
  2. 第二次CAS设置tail,成功后才设置t.next = node

为什么不先设t.next再CAS?因为如果CAS失败,t.next已经被设置了,但tail没变,其他线程可能看到不一致的状态。先CAS成功再修改next,保证原子性。

addWaiter:enq的封装

private Node addWaiter(Node mode){    Node node = new Node(Thread.currentThread(), mode);    Node pred = tail;if (pred != null) {        node.prev = pred;if (compareAndSetTail(pred, node)) {            pred.next = node;return node;        }    }    enq(node);  // 竞争失败或初始化,走enqreturn node;}

七、Condition的实现:独占模式下的等待/通知

ConditionObject是AQS的内部类,本质是一个独立的CLH队列:

publicclassConditionObjectimplementsConditionjava.io.Serializable{privatestaticfinallong serialVersionUID = 1173984872572414699L;// Condition队列的头privatetransient Node firstWaiter;// Condition队列的尾privatetransient Node lastWaiter;publicfinalvoidawait()throws InterruptedException {if (Thread.interrupted())thrownew InterruptedException();        Node node = addConditionWaiter();  // 加入Condition队列int savedState = fullyRelease(node);  // 释放锁,state=0int interruptMode = 0;while (!isOnSyncQueue(node)) {  // 不在AQS主队列时park            LockSupport.park(this);if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)break;        }if (acquireQueued(node, savedState) && interruptMode != THROW_IE)            interruptMode = REINTERRUPT;if (node.nextWaiter != null// clean up            unlinkCancelledWaiters();if (interruptMode != 0)            reportInterruptAfterWait(interruptMode);    }private Node addConditionWaiter(){        Node t = lastWaiter;if (t != null && t.waitStatus != Node.CONDITION) {            unlinkCancelledWaiters();            t = lastWaiter;        }        Node node = new Node(Thread.currentThread(), Node.CONDITION);if (t == null)            firstWaiter = node;else            t.nextWaiter = node;        lastWaiter = node;return node;    }publicfinalvoidsignal(){if (!isHeldExclusively())thrownew IllegalMonitorStateException();        Node first = firstWaiter;if (first != null)            doSignal(first);  // 移动到AQS主队列    }privatevoiddoSignal(Node first){do {if ((firstWaiter = first.nextWaiter) == null)                lastWaiter = null;            first.nextWaiter = null;        } while (!transferForSignal(first) &&                 (first = firstWaiter) != null);    }finalbooleantransferForSignal(Node node){if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))returnfalse;  // CAS失败,说明已被取消或signal        Node p = enq(node);  // 加入AQS主队列int ws = p.waitStatus;if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))            LockSupport.unpark(node.thread);returntrue;    }}

关键设计:

  • Condition队列节点的waitStatus=CONDITION(-2),不在主队列的唤醒逻辑中
  • await时先释放锁(fullyRelease),再park——保证不占着锁等待
  • signal时把节点从Condition队列移到AQS主队列尾部,然后unpark

八、cancelAcquire:取消节点的清理

privatevoidcancelAcquire(Node node){if (node == null)return;    node.thread = null;  // 断引用    Node pred = node.prev;if (pred == null)return;    node.waitStatus = Node.CANCELLED;if (node.next == null || node.next.waitStatus > 0) {// 找到有效后继        Node s = node.next;if (s == null)for (Node t = tail; t != null && t != node; t = t.prev)if (t.waitStatus <= 0)                    s = t;if (s != null)            compareAndSetNext(pred, node, s);    } else {// 后继正常,前驱跳过自己指向后继if (pred != null && pred.waitStatus <= 0) {            Node succ = node.next;if (succ != null && succ.waitStatus <= 0)                compareAndSetNext(pred, node, succ);        }    }}

双向链表删除的难点:多线程同时操作,用CAS逐个更新next指针,而非一次性断开——保证线程安全。


九、shouldParkAfterFailedAcquire:park前的最后判断

privatestaticbooleanshouldParkAfterFailedAcquire(Node pred, Node node){int ws = pred.waitStatus;if (ws == Node.SIGNAL)returntrue;   // 前驱已标记SIGNAL,可以安全parkif (ws > 0) {      // 前驱CANCELLEDdo {            node.prev = pred = pred.prev;        } while (pred.waitStatus > 0);  // 跳过所有取消节点        pred.next = node;    } else {        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);  // 给前驱标记SIGNAL    }returnfalse;  // 标记完SIGNAL后再自旋一次,不park}

为什么标记SIGNAL后不立即park? 因为CAS可能失败(其他线程已经在改pred的waitStatus了),需要再循环一次确认。这就是"惰性标记"——不到park那一刻不做多余的事。


十、总结一下

10.1 为什么是CLH而不是MCS?

特性
CLH
MCS
节点存储
线程本地(prev引用前驱)
每个节点存后继引用
缓存友好性
差(prev可能在不同cache line)
好(每个节点只访问本地字段)
实现复杂度
低(只需prev)
高(需要一个hint指针)
AQS选择原因
AQS需要从tail快速找到head附近操作,prev够用

AQS选CLH是因为:需要从tail向前扫描(找有效节点),prev引用天然支持。MCS的hint机制在这种场景下没有优势。

10.2 为什么用volatile而非synchronized?

AQS队列操作完全依赖volatile+CAS,没有任何synchronized块。原因:

  • synchronized在竞争激烈时涉及内核态切换,开销大
  • volatile+CAS在无竞争时是单指令,有竞争时自旋,延迟更可控
  • 但代价是代码复杂度极高——AQS源码约2000行,全是细节

10.3 为什么state用int而非AtomicInteger?

  • int+CAS比AtomicInteger少一层对象头和volatile字段的间接访问
  • AQS自己封装了compareAndSetState,可以在失败后加自旋逻辑
  • 独占模式下state还表示重入次数(可>1),语义上不适合AtomicInteger的"单一值"语义

10.4 模板方法模式的精髓

// AQS定义骨架publicabstractclassAbstractQueuedSynchronizer{protectedbooleantryAcquire(int)thrownew UnsupportedOperationException(); }protectedbooleantryRelease(int)thrownew UnsupportedOperationException(); }protectedinttryAcquireShared(int)thrownew UnsupportedOperationException(); }protectedbooleantryReleaseShared(int)thrownew UnsupportedOperationException(); }// 子类只需实现这四个方法,其余全部复用publicfinalvoidacquire(int arg){ ... }publicfinalvoidrelease(int arg){ ... }}

ReentrantLock只需实现tryAcquiretryRelease,Semaphore只需实现tryAcquireSharedtryReleaseShared。这是"策略模式+模板方法"的经典结合——AQS负责队列管理、park/unpark、CAS,子类只负责什么时候能拿到锁。