Java 面试题

AQS原理:ReentrantLock底层到底怎么工作

"AQS是怎么工作的?ReentrantLock怎么用AQS实现的?"

为什么面试官会问这道题

AQS是j.u.c锁的基石。中高级Java面试用这题看你能不能读JDK源码,理解标准库里所有锁、信号量、闭锁的底层原语。

如何回答

  1. 1

    AQS核心:一个volatile int state + FIFO的CLH队列。子类解释state的含义——ReentrantLock当重入计数、Semaphore当许可数、CountDownLatch当剩余计数。

  2. 2

    acquire流程:tryAcquire子类实现(典型是CAS改state),失败就入队park。release流程:tryRelease子类实现,成功就unpark队头的next节点。

  3. 3

    CLH队列:Node双向链表,每个Node一个等待线程,有prev/next和waitStatus。head是哨兵,线程在非head节点等。

  4. 4

    ReentrantLock是独占模式。tryAcquire CAS state从0→1抢锁;同线程重入++state。tryRelease--state,归零释放并唤醒下一个等待者。

  5. 5

    其他基于AQS的:Semaphore是共享模式(多个acquire能同时成功)、CountDownLatch是state=count的共享、ReentrantReadWriteLock把读计数和写计数打包进一个int state。

参考回答示例

AQS(AbstractQueuedSynchronizer)是j.u.c几乎所有锁类的共享底层。它提供两个东西:volatile int state和FIFO的CLH风格队列。子类定义state的含义。ReentrantLock用state当重入计数。Semaphore用它当剩余许可。CountDownLatch用它当剩余倒计数。acquire流程:线程调tryAcquire(子类实现,典型是CAS改state——ReentrantLock就是CAS state从0到1)。tryAcquire成功就拿到资源。失败就把当前线程包成Node入CLH队列,LockSupport.park()挂起。release流程:持有者调tryRelease,子类实现(ReentrantLock是--state,归零时把独占owner置null);tryRelease成功且队列非空,AQS unpark队头的next节点,该节点醒来重跑tryAcquire,成功就接手。CLH队列细节:Node双向链表,每个Node有prev、next、thread、waitStatus。head是哨兵节点,真正第一个等待者是head.next。等待时线程先自旋再park。取消或中断时节点标记cancelled最终被splice出去。ReentrantLock有两个内部类——FairSync和NonfairSync。默认非公平会插队——新线程可以直接tryAcquire抢锁,即使队列里有人在等。公平的tryAcquire先查hasQueuedPredecessors(),有人排队就不尝试抢。非公平吞吐高,公平防止饥饿。ReentrantReadWriteLock用了个巧妙技巧——state的低16位是写计数、高16位是读计数,一次CAS能同时操作两个。懂了AQS就能读JDK里j.u.c的任何锁源码,也能通过继承AQS自己造同步器。

实用技巧

  • state含义由子类定义——讲这个显示真的读过源码。

  • 公平和非公平的区别是经典追问——要会讲插队。

  • 能讲ReentrantReadWriteLock把读写计数packed到一个int里是加分项。

  • 忘了CLH、tryAcquire/tryRelease这些名字,即答侠可以实时提示。

常见问题

为什么用CLH不用MCS?

CLH更好适配AQS的取消模型——节点可以干净地splice出去。双向链表的prev指针是取消能正常工作的关键。

为什么park不忙等?

park释放CPU。忙等浪费周期、引起cache一致性流量。AQS在park前会短暂自旋,短等场景省下park开销。

能自己扩AQS写锁吗?

能——继承AbstractQueuedSynchronizer或AbstractQueuedLongSynchronizer,实现tryAcquire/tryRelease。自定义同步器就是这么造的。

面试时担心忘词?即答侠实时助你

即答侠 AI 实时监听面试对话,自动识别问题并即时生成回答建议——无感辅助,让你从容应对每一道题。

免费试用即答侠