reentrantlock 的重入特性是其天然核心能力,同一线程可多次获取同一锁,state 计数器增减管理,无需显式开启;公平性参数不影响重入;重入支持递归、分层调用等场景,但也需警惕锁粒度粗化、unlock 次数不匹配及 condition 复杂性。

Java 中 ReentrantLock 的重入特性不是“选择实现”的,而是它天然具备的核心能力——你用它,默认就支持重入;不希望重入,反而需要额外规避。关键在于理解重入机制如何工作,以及它在实际锁逻辑中带来的影响和注意事项。
重入特性是默认行为,无需显式开启
ReentrantLock 构造时传入 false(非公平)或 true(公平)只影响线程获取锁的调度策略,不影响重入能力。只要同一线程已持有该锁,再次调用 lock() 就会成功,计数器 +1;对应地,每次 unlock() 计数器 -1,直到归零才真正释放锁。
- 重入次数由内部同步状态(state)维护,线程身份通过
Thread.currentThread()判断 - 即使嵌套调用发生在不同方法、不同层次(如 public 方法调用 private 辅助方法),只要线程相同,就可重入
- 尝试用其他线程去 unlock 一个已被重入多次的锁,会抛出
IllegalMonitorStateException
何时需要依赖重入?典型场景
重入特性让递归调用、分层加锁、组合式资源管理更安全自然:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 递归算法加锁:比如树遍历中每个节点操作都需加同一把锁,递归进入子节点时不因重复 lock 而阻塞或死锁
-
方法拆分与复用:A 方法加锁后调用 B 方法,而 B 方法内部也执行
lock()/unlock()—— 若无重入,B 的 lock 会阻塞自己 -
封装可重入的工具类:例如自定义缓存类中,
get()和refresh()都需保证线程安全,且refresh()可能被get()触发,重入避免嵌套死锁
何时要警惕重入带来的隐患?
重入虽方便,但可能掩盖设计问题或引发意外行为:
- 锁粒度变粗却不自知:外层方法持锁调用内层方法,内层又加同一把锁,整个调用链实际被一把锁串行化,可能成为性能瓶颈
- unlock 次数必须严格匹配 lock 次数:少 unlock 会导致锁永远无法释放;多 unlock 会抛异常。建议始终用 try-finally 或 try-with-resources(配合自定义 CloseableLock 封装)确保成对
- 条件变量(Condition)绑定的是锁,不是调用栈:await() 会释放所有重入次数,signal() 唤醒后需重新竞争并恢复全部重入层级,逻辑复杂时易出错
不想要重入?那就不用 ReentrantLock
Java 标准库没有“非重入锁”的内置实现。如果业务明确禁止同一线程重复获取锁(例如检测逻辑错误或强制分段加锁),可:
- 用
synchronized替代——它也是重入的,同样不满足“非重入”需求 - 自行封装一个基于 AtomicBoolean 的简单互斥锁(仅允许 acquire 一次),但失去等待、超时、中断等高级能力
- 更合理的思路是重构代码:用细粒度锁、读写锁(ReentrantReadWriteLock)、或无锁结构(如 CAS + volatile)替代“需要非重入”的场景
重入不是开关选项,而是 ReentrantLock 的本质特征。真正要做的,是看清业务是否真的受益于重入,还是被它悄悄掩盖了并发模型的问题。合理利用它,比试图绕过它更有价值。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










