reentrantlock底层基于aqs实现:用volatile int state表示锁状态(0为空闲,>0为重入次数),通过clh双向队列管理等待线程,cas保证原子性;公平与非公平锁差异仅在于tryacquire是否插队;unlock通过state减1和唤醒后继节点完成。

ReentrantLock 的底层核心是 AQS(AbstractQueuedSynchronizer),它不直接处理锁逻辑,而是提供一套标准化的同步框架:用 volatile int state 表示锁状态,用 双向链表构成的 CLH 队列 管理等待线程,并通过 CAS 原子操作 保证状态变更的安全性。ReentrantLock 本身只是 AQS 的一个具体使用者,真正干活的是它的内部类 Sync 及其子类 FairSync 和 NonfairSync。
state 状态变量控制锁的获取与重入
AQS 中的 state 是一个 volatile 整型字段,ReentrantLock 将其语义定义为:
- state == 0:锁空闲,无任何线程持有
- state > 0:锁被占用,值即当前持有线程的重入次数
- 每次 lock() 成功,state 加 1;每次 unlock() 成功,state 减 1;仅当 state 归零时,锁才真正释放
这种设计天然支持可重入:同一线程再次调用 lock() 时,AQS 不会阻塞它,而是检查 exclusiveOwnerThread 是否为当前线程,是则直接更新 state,无需竞争。
CLH 同步队列管理阻塞与唤醒
当线程抢锁失败,AQS 就把它封装成 Node 节点,插入到一个 FIFO 双向链表(CLH 变种)尾部:
- head 是虚拟头节点,不关联任何线程,只作哨兵;tail 指向队尾实际等待的线程
- 每个 Node 包含 waitStatus 字段,用于标记线程状态(如 SIGNAL 表示需被唤醒、CANCELLED 表示已取消)
- 线程在 Node 上调用 LockSupport.park() 进入阻塞;前驱节点释放锁后,会唤醒后继节点的线程
这个队列不是用来“排队抢锁”,而是用来“排队等通知”——只有前驱节点释放锁并显式唤醒,后继节点才有机会重新尝试获取锁。
公平锁与非公平锁的关键区别在 tryAcquire
两者共用同一套 AQS 队列和 state 机制,差异只体现在加锁入口逻辑:
- 非公平锁(默认):调用 lock() 时先 CAS 尝试抢占(即“插队”),成功就直接拿锁;失败才进队列
- 公平锁:调用 lock() 时跳过抢占,直接走 acquire 流程,先检查队列是否为空或自己是否排在队首,再尝试获取
- 也就是说,公平性不是靠队列本身保证的,而是靠 tryAcquire 实现中是否做“队首检查”这一判断
unlock 的本质是 state 减 1 与条件唤醒
unlock() 最终调用 AQS 的 release 方法:
- 先调用 tryRelease:检查当前线程是否为持有者,是则 state 减 1;减完若 state == 0,表示锁完全释放,返回 true
- release 返回 true 后,AQS 会唤醒 head.next 对应的节点(即队列中第一个等待线程)
- 被唤醒的线程回到 acquire 流程,再次调用 tryAcquire 尝试获取锁
注意:unlock 必须由持有锁的线程调用,否则抛 IllegalMonitorStateException;且必须配对使用,漏调会导致其他线程永久阻塞。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











