ReentrantLock 不会出现因递归重入超 Integer.MAX_VALUE 导致的异常,因其 state 溢出是静默的且未定义但不抛异常;实际中栈溢出、内存耗尽等会先发生。

ReentrantLock 不会出现递归重入超过 Integer.MAX_VALUE 导致的异常。
重入计数用的是 int 类型,但实际不可能达到 Integer.MAX_VALUE
ReentrantLock 内部通过 Sync(继承自 AbstractQueuedSynchronizer)维护一个 state 字段,类型为 int,用于记录当前线程的重入次数。每次重入,state 加 1;每次释放,减 1。
理论上,当同一线程连续加锁 Integer.MAX_VALUE + 1 次时,state 会溢出变为 Integer.MIN_VALUE,但这在现实中几乎不可发生,原因如下:
- 每次 lock() 调用都涉及 CAS 操作、可能的线程调度和栈帧压入,单线程在合理时间内无法执行数十亿次调用;
- JVM 栈空间有限,深度递归或循环重入很快触发
StackOverflowError,远早于state溢出; - 即使采用非递归方式循环调用
lock(),JVM 内存、CPU 时间或操作系统线程调度限制也会先导致 OOM 或超时等失败。
溢出后行为是未定义但不会主动抛出特定异常
state 是一个普通 int 字段,Java 对整数溢出不做检查,也不会自动抛出异常。溢出后,state 变为负值(如从 2147483647 + 1 → -2147483648),此时:
- 后续
unlock()会继续减小该值,仍保持数学一致性(只要加锁/解锁匹配); - 但由于
state ,<code>isHeldExclusively()等逻辑仍能正常工作——AQS 只关心是否 > 0 表示持有,不校验是否“过大”; - 没有代码路径会在
state溢出时显式 throw 异常(比如IllegalMonitorStateException是在 unlock 未持有锁时抛出,与溢出无关)。
真正会提前失败的常见场景
开发者更可能遇到以下真实问题,而非整数溢出:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 忘记 unlock():导致锁永远无法释放,其他线程阻塞;
-
unlock() 次数多于 lock():触发
IllegalMonitorStateException; -
不同线程交叉 unlock():同样抛
IllegalMonitorStateException; -
深度递归调用 lock():先抛
StackOverflowError; - 无限循环调用 lock() 且无 unlock():最终耗尽内存或被 OS 终止,而非整数溢出异常。
源码佐证:AQS 中 state 的使用无溢出检查
查看 AbstractQueuedSynchronizer 的 tryAcquire 和 tryRelease 实现(ReentrantLock.Sync 复写):
加锁逻辑只判断 getState() == 0(首次获取)或 getExclusiveOwnerThread() == current(重入),然后执行 setState(c + 1) —— 这里没有对 c + 1 做范围检查;
解锁逻辑计算 nextc = c - 1,若为 0 则清空 owner,否则 setState(nextc) —— 同样无下限检查。
也就是说,JVM 整数溢出是静默的,ReentrantLock 也未做额外防护,因为它不认为这是需拦截的错误情形。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










