StampedLock 的 readLock() 和 writeLock() 不响应中断,线程被中断后因未保存中断状态而陷入无限 CPU 自旋,导致 100% 占用;应改用 readLockInterruptibly() 等可中断方法规避。

StampedLock 的 readLock() 和 writeLock() 方法本身不响应中断,但当线程处于阻塞等待状态时被调用 interrupt(),会触发内部自旋重试逻辑失控,导致 CPU 占用率飙升至 100%。这不是设计意图,而是 JDK 实现中未正确处理中断状态的缺陷。
中断未被保存与恢复,引发无限自旋
StampedLock 内部使用类似 CAS 的忙等待机制获取锁。当线程在 park 状态被中断时,Unsafe.park() 会立即返回,但 StampedLock 没有检查或保存中断状态,而是直接进入下一轮循环尝试获取锁 —— 从而退化为纯 CPU 自旋,不再挂起线程。
- 中断标志被忽略,线程无法退出等待逻辑
- 每次 park 返回后都重新尝试 CAS,无休止执行
- 尤其在写锁被长期占用、多个读线程同时被中断时,多个核心可能被迅速打满
问题只出现在非 interruptible 方法上
只有明确不承诺响应中断的接口才会触发该行为,包括:
-
readLock()(阻塞式悲观读) -
writeLock()(阻塞式写锁) -
tryReadLock()和tryWriteLock()不受影响(它们不阻塞)
而配套的可中断版本是安全的:readLockInterruptibly() 和 writeLockInterruptibly() 内部做了中断状态检查与传播,不会陷入自旋。
典型复现场景
以下组合极易暴露该 Bug:
- 一个线程持写锁长时间不释放(如
LockSupport.parkNanos(6s)) - 多个线程调用
readLock()阻塞等待 - 在等待期间对这些线程调用
interrupt() - 结果:被中断的读线程持续占用 CPU,top 显示对应核心 100% 使用率
规避方案与修复建议
目前没有官方热修复,生产环境应主动规避:
- 避免对调用
readLock()/writeLock()的线程执行interrupt() - 必须支持中断时,统一改用
readLockInterruptibly()或tryReadLock(long, TimeUnit) - 若需兼容旧逻辑,可在外围加超时判断 + 主动退出,而非依赖中断信号
- JDK 9+ 仍未修复此问题,属于已知未优先处理的底层实现瑕疵
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











