ReentrantLock源码:从“抢座位“到AQS队列的底层揭秘 📅 发布时间:2026/7/8 18:55:23 👁️ 浏览次数: 在多线程并发编程中我们都学过synchronized关键字但它就像黑盒子的自动锁你不知道谁在排队不能中断等待也无法设置超时。2004年Java 5引入的ReentrantLock可重入锁底层依托AbstractQueuedSynchronizerAQS将锁的本质从系统级隐式管理变成了对象级显式控制。今天我们从源码层面拆解这行代码背后发生了什么lock.lock(); // 这行代码到底垄断了什么资源一、核心架构AQS——锁的大后台ReentrantLock本身并不直接实现锁逻辑而是委托给内部类Sync继承自AQS。这就像消息队列前两篇文章提到的MQ将存储引擎和协议解耦。1.1 AQS的三根顶梁柱AQSAbstractQueuedSynchronizer是一个CLH变体队列状态机的框架核心字段类型作用类比statevolatile int资源计数器0无人占用1被占1重入次数像在餐厅占座一个人占座但点了3道菜exclusiveOwnerThreadThread独占线程标记记录当前谁拿着锁老板的名字写在座位上head/tailNode双向队列排队等待的线程等位区的顾客名单// AQS核心结构位于java.util.concurrent.locks.AbstractQueuedSynchronizerpublic abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer { private volatile int state; // 同步状态 private transient volatile Node head; // 队头虚节点 private transient volatile Node tail; // 队尾 // ...}二、加锁源码走读非公平锁的抢票逻辑ReentrantLock默认是非公平锁NonfairSync允许插队。这像不像你消息队列那篇文章里说的流量削峰不这更像是有人不排队直接挤到窗口前。2.1 第一次CAS尝试快速路径// ReentrantLock.NonfairSync.lock()final void lock() { // 1. 先无脑CAS抢一次0→1成功就占有锁 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); // 失败就进入正常流程}图解就像进餐厅先看有没有空桌state0有就直接坐CAS成功没有才取号排队。2.2 acquire的标准流程AQS模板方法如果第一次没抢到进入acquire(1)这是模板方法模式// AQS.acquire() - 模板方法定义了加锁流程框架public final void acquire(int arg) { // 1. tryAcquire子类实现尝试获取 // 2. addWaiter入队包装成Node加入CLH队列尾部 // 3. acquireQueued在队列里自旋阻塞等待 if (!tryAcquire(arg) acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt();}步骤1tryAcquire——子类的个性定制NonfairSync的实现// 非公平锁的尝试获取protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires);} final boolean nonfairTryAcquire(int acquires) { final Thread current Thread.currentThread(); int c getState(); if (c 0) { // 哪怕有队列也再CAS抢一次非公平的精髓 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current getExclusiveOwnerThread()) { // **可重入性**同一线程再次获取state1 int nextc c acquires; if (nextc 0) // overflow throw new Error(Maximum lock count exceeded); setState(nextc); return true; } return false;}关键设计可重入通过判断current owner避免自己把自己锁死就像你家门锁你拿钥匙进去后在屋里可以再锁一次而不会卡死非公平c0时直接CAS不看队列前面有没有人步骤2addWaiter——封装成节点入队private Node addWaiter(Node mode) { Node node new Node(Thread.currentThread(), mode); Node pred tail; // 快速入队CAS设置tail if (pred ! null) { node.prev pred; if (compareAndSetTail(pred, node)) { pred.next node; return node; } } enq(node); // 竞争失败后的慢速入队自旋CAS return node;}结构特性双向链表prev前驱和next后继虚拟头节点head始终是一个Thread为null的占位节点哨兵节点步骤3acquireQueued——队列中的自旋阻塞final boolean acquireQueued(final Node node, int arg) { boolean failed true; try { boolean interrupted false; for (;;) { // 自旋spin final Node p node.predecessor(); // 只有前驱是head时才有资格tryAcquire防止全面惊群 if (p head tryAcquire(arg)) { setHead(node); // 抢到后把自己设为虚head p.next null; // help GC failed false; return interrupted; } // shouldParkAfterFailedAcquire检查是否需要睡觉 // parkAndCheckInterrupt挂起线程调用LockSupport.park if (shouldParkAfterFailedAcquire(p, node) parkAndCheckInterrupt()) interrupted true; } } finally { if (failed) cancelAcquire(node); // 异常时清理节点 }}精妙之处只有前驱是head才尝试获取避免所有线程一起抢与synchronized的惊群效应相比这像消息队列的顺序消费自旋→阻塞先自旋几次短时等待实在拿不到才调用LockSupport.park()挂起切换到内核态省CPU三、解锁源码state减到0才算真释放加锁是state解锁就是state--减到0才算真正释放。// ReentrantLock.unlock() → AQS.release()public final boolean release(int arg) { if (tryRelease(arg)) { // 1. 先尝试释放子类实现 Node h head; // 2. 如果队列不为空且需要唤醒waitStatus≠0叫醒后继 if (h ! null h.waitStatus ! 0) unparkSuccessor(h); return true; } return false;}tryRelease在Sync中的实现protected final boolean tryRelease(int releases) { int c getState() - releases; // 减去释放量通常是1 // 只有锁的持有者才能释放防君子不防小人但也判一下 if (Thread.currentThread() ! getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free false; if (c 0) { // 完全释放重入计数归零 free true; setExclusiveOwnerThread(null); // 擦除 owner 标记 } setState(c); // 不是volatile操作不setState内部是volatile写 return free;}唤醒机制private void unparkSuccessor(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; for (Node t tail; t ! null t ! node; t t.prev) if (t.waitStatus 0) s t; // 从后往前找最近的正常节点 } if (s ! null) LockSupport.unpark(s.thread); // 叫醒排队最靠前的那个}注意唤醒是从后往前找有效节点因为next指针可能还没设置好enq时是先设prevCAS tail再设next而prev是稳定的。四、公平锁 vs 非公平锁要不要走后门就像你消息队列文章里说的应用解耦这里AQS通过模板方法解耦了排队策略维度NonfairSync默认FairSynctryAcquire逻辑c0时直接CAS抢c0时先检查hasQueuedPredecessors()有排队线程就让性能吞吐量大线程切换少吞吐量略低但防止饥饿使用场景普通业务锁如计数器避免线程饥饿如文件IO等待公平锁的额外判断// FairSync.tryAcquireif (c 0) { // 关键差别先看有没有前驱节点在排队 if (!hasQueuedPredecessors() compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; }}五、与synchronized的终极对决特性ReentrantLocksynchronizedJVM层面实现层Java代码SDK CAS LockSupportJVM monitorenter/exit 对象头Mark Word等待队列显式AQS双向队列对象头的_WaitSetC实现不可见可中断lockInterruptibly()支持中断不可中断Io阻塞除外超时tryLock(long timeout, TimeUnit unit)不支持条件队列多条件变量newCondition()可创建多个单一wait/notifyAll性能Java 6后两者性能接近AQS的优化JIT编译后偏向锁/轻量级锁优化六、总结AQS的设计哲学ReentrantLock的源码其实展示了模板方法模式在并发框架中的极致应用状态与队列分离state管资源Node管排队职责单一CAS自旋优化先用用户态自旋抢不行再进内核态park避免线程切换开销可重入友好state计数器天然支持同线程递归加锁变体灵活公平/非公平只需覆盖tryAcquire复用AQS的排队基础设施消息队列文章中说的引入MQ之后系统和系统间只需要依赖于MQ而无需进行直接依赖。 AQS也是如此——所有并发工具Semaphore、CountDownLatch、ReentrantReadWriteLock只需依赖AQS的状态和队列机制而不必重复造轮子。
解锁7大免费工具:突破内容访问限制完全指南 解锁7大免费工具:突破内容访问限制完全指南 【免费下载链接】bypass-paywalls-chrome-clean 项目地址: https://gitcode.com/GitHub_Trending/by/bypass-paywalls-chrome-clean 在信息爆炸的时代,优质内容往往被付费墙阻隔,成为获取知… 2026/7/7 1:21:57
Kali Linux应急响应实战:从SSH连接到系统信息获取的完整流程 Kali Linux应急响应实战:从SSH连接到系统信息获取的完整流程 最近在和一些刚接触网络安全的朋友交流时,发现一个挺有意思的现象。很多人一提到应急响应或者CTF比赛,脑子里立刻浮现出各种复杂的工具和眼花缭乱的攻击链,却往往忽略了… 2026/7/8 6:48:10
【计算机网络】TCP 协议深度拆解 TCP(传输控制协议)在面试中出现的评频率也很高,为了自己的复习和帮助大家备战面试,今天特别把TCP核心的知识点串起来过一遍。今天我从 TCP 是什么、优缺点、长什么样(报文组成),一路深挖到它的连… 2026/7/5 7:14:28
Codex不是ChatGPT:VSCode深度集成的代码语义理解实践 1. 项目概述:Codex 不是 ChatGPT 的“马甲”,而是开发者专属的代码理解引擎 Codex 这个词最近在技术圈里反复刷屏,但很多人点开搜索结果后反而更迷糊了——它和 ChatGPT 到底什么关系?VSCode 里装的那个 Codex 插件,是… 2026/7/8 18:51:49
2026年Codex Desktop丝滑配置指南:中文支持与API双通道实战 1. 项目概述:为什么2026年还在谈Codex Desktop的“丝滑使用”? Codex Desktop不是新软件,但2026年它在国内的真实使用体验,和三年前相比,已经发生了本质变化——不是技术退步了,而是生态位彻底重构了。我从… 2026/7/8 18:51:49
卒中康复期疲劳自动判别工具:PyTorch版3D-CNN模型+带标注视频数据+一键推理界面 本文还有配套的精品资源,点击获取 简介:直接可用的卒中后疲劳识别工具包,用3D-CNN处理患者行为或生理视频序列,自动输出疲劳状态判断结果。含完整标注数据集(视频帧序列)、已训练好的C3D模型权重、train… 2026/7/8 18:49:48
肌电信号驱动的软体捏握外骨骼技术解析 1. 这不是机械臂,而是一层“会呼吸”的肌肉外衣你有没有试过连续拧开十瓶矿泉水盖?手指发酸、虎口胀痛、指尖发麻——这种疲劳感,不是来自力量不足,而是神经与肌肉之间那条微弱信号通路的“带宽瓶颈”。EMG-Driven Soft Exoskelet… 2026/7/8 18:49:48
21天学pcie--TLP 长什么样?(不看协议也能懂) 目录 第 8 篇:TLP 长什么样?(不看协议也能懂) 一、先给一句话结论(请记住) 二、为什么要有 TLP? 三、TLP 在 PCIe 协议栈中的位置 2026/7/8 18:47:48
MP2672A双节锂电池充电管理IC详解与应用 1. MP2672A芯片深度解析MP2672A是MPS公司推出的一款高度集成的双节锂离子电池充电管理IC,采用QFN-18(2mmx3mm)紧凑封装。这款芯片在便携式设备电源管理领域具有显著优势,其核心特性包括:输入电压范围:4V至5… 2026/7/8 18:47:48
BetterNCM安装器:高效管理网易云插件的最佳选择 BetterNCM安装器:高效管理网易云插件的最佳选择 【免费下载链接】BetterNCM-Installer 一键安装 Better 系软件 项目地址: https://gitcode.com/gh_mirrors/be/BetterNCM-Installer 还在为网易云音乐插件的繁琐安装流程而烦恼吗?BetterNCM安装器是… 2026/7/8 0:02:48
运动控制系统安全设置对比:ECI3808的3种限位保护与急停逻辑实现 运动控制系统安全机制深度解析:限位保护与急停逻辑的设计哲学在精密制造与自动化领域,运动控制系统的安全设计绝非简单的功能堆砌,而是一套融合了机械工程、电气原理和软件算法的防御体系。当一台数控机床以每分钟数万转的速度运转࿰… 2026/7/8 0:06:48
AI大模型应用开发:小白也能抓住的红利风口,收藏这篇入门指南! 文章指出,虽然微软等科技巨头在裁员,但英伟达等公司却在积极扩招AI相关人才,尤其是具身智能、仿真等领域。AI行业正在经历结构性调整,传统岗位被淘汰,而大模型应用开发等新岗位需求旺盛。对于想转行或学习AI的普通人来… 2026/7/8 0:10:49
6个月转型AI工程师:实战路径与核心技能 1. 项目概述:6个月转型AI工程师的可行性路径在2023年大模型技术爆发的背景下,AI工程师岗位需求同比增长217%(LinkedIn数据)。不同于传统算法工程师需要3-5年培养周期,现代AI工程师更侧重工程化落地能力。我在硅谷科技公… 2026/7/7 11:26:57
TPAFE0808与PIC18F87K22的多通道信号采集方案 1. 项目背景与核心需求在工业自动化、医疗设备和科研仪器等领域,多通道信号采集与系统监测是基础且关键的技术需求。传统方案往往面临通道数量不足、信号调理复杂、系统集成度低等问题。TPAFE0808作为一款8通道模拟前端芯片,与PIC18F87K22微控制器的组合… 2026/7/7 11:26:57
STC3115与PIC18LF26K80构建高精度电池管理系统 1. STC3115与PIC18LF26K80在电池管理系统中的核心价值在现代电子设备中,电池管理系统(BMS)的重要性不亚于设备的核心处理器。STC3115作为一款高精度电池电量监测IC,与PIC18LF26K80微控制器的组合,构成了一个既能精确监控又能智能管理的完整解… 2026/7/8 14:25:08