一、AQS 的简介

AQS = AbstractQueuedSynchronizer(抽象队列同步器)是 JUC 锁(ReentrantLock、Semaphore、CountDownLatch 等)的底层核心骨架

它只干三件事:

  1. 用一个 state 变量表示锁状态(0 = 无锁,1 = 已加锁,>1 = 重入)
  2. 用一个 exclusiveOwnerThread 记录当前持有锁的线程
  3. 用一个 CLH 变体双向队列 存放抢不到锁的线程

简单记:state(状态) + 队列(等待) + CAS(抢锁) = AQS


二、AQS 核心结构

state: int          // 锁状态
exclusiveOwnerThread: Thread  // 持有锁的线程
waiter: Node        // 等待队列头尾指针

Node 节点里存:

  • 线程对象
  • 等待状态(CANCELLED / SIGNAL / CONDITION 等)
  • prev /next 指针(双向链表)

三、ReentrantLock 非公平锁加锁流程(最常考)

1.lock () 完整流程

  1. 第一次 CAS 尝试把 state 从 0 → 1

    • 成功:直接拿到锁,设置 exclusiveOwnerThread = 当前线程
    • 失败:进入下一步
  2. 判断是不是锁重入当前线程 == 持有锁线程?

    • 是:state + 1(可重入),直接返回
    • 否:进入下一步
  3. 加入 AQS 等待队列

    • 封装成 Node 节点,尾插法入队
    • 再尝试一次抢锁(看前节点是不是 head)
  4. 抢不到就阻塞

    • 调用 LockSupport.park(this) 线程休眠
    • 等待被唤醒

2.解锁流程 unlock ()

  1. state - 1
  2. 如果 state == 0:
    • 清空持有线程 exclusiveOwnerThread = null
    • 唤醒队列队首第二个节点(head.next)
  3. 如果 state > 0:
    • 说明是重入,只减计数,不唤醒

四、公平锁 vs 非公平锁

  • 非公平锁(默认)上来直接 CAS 抢,不看队列,抢到就用 → 吞吐量高
  • 公平锁上来先看队列有没有人排队 → 有就乖乖排队,绝不插队 → 不会线程饥饿,但慢一点

五、一张图看懂整套流程

线程调用 lock()
     ↓
CAS(state:0→1) 成功?
├─是 → 持有锁,结束
└─否 → 判断是不是重入
        ├─是 → state++,结束
        └─否 → 封装 Node 入队
                ↓
                再试抢锁
                ↓
        抢不到 → park() 阻塞
                ↓
被前节点唤醒 → 再次抢锁
                ↓
        抢到 → 出队、执行业务
                ↓
     unlock() → state--
                ↓
   state=0 → 唤醒后继节点

六、面试必背 5 句 AQS 核心总结

  1. AQS 是 JUC 锁的底层框架,靠 state + 队列 + CAS 实现
  2. ReentrantLock 底层就是 AQS 的独占模式
  3. 加锁:CAS 抢锁 → 重入判断 → 入队阻塞
  4. 解锁:state 递减 → 唤醒队列线程
  5. 公平锁看队列,非公平锁直接抢

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐