java自适应自旋锁由jvm动态调节:依据锁历史成功率、持有线程状态(runnable才鼓励自旋)及系统cpu负载实时调整自旋次数,仅作用于轻量级锁cas失败后、升级前的忙等阶段,不改变锁状态且各锁独立决策。

Java 中自适应自旋锁不是靠固定参数拍板决定“转几次”,而是 JVM 在运行时持续观察同一把锁的历史行为,边试边学、实时反馈,动态调节本次该忙等多久。
看这把锁最近成没成
JVM 为每个锁对象(准确说是其关联的 ObjectMonitor)维护一个隐式成功率指标。每次自旋成功拿到锁,系统就记一笔;失败进入阻塞队列,也记一笔。比如最近 5 次尝试里有 4 次成功,成功率 80%,下次就会倾向多给几次机会;如果连续两次都转满上限也没抢到,JVM 就会把它标记为“难搞的锁”,后续直接跳过自旋,走重量级锁流程。
- 成功率高 → 自旋次数温和上浮(如 ×1.5 或 +2~3),但有软上限(通常几百次内)
- 成功率低 → 次数快速衰减(如 ÷2),下限可归零,直接阻塞
- 调整基于加权移动平均,新结果影响更大,避免被偶然波动带偏
看拿着锁的线程还在不在跑
持有锁的线程状态是关键信号:如果它当前是 RUNNABLE 状态(没被挂起、没 sleep、没做 IO),说明很可能马上释放锁,值得多等几轮;但如果它已进入 BLOCKED、WAITING 或 TIMED_WAITING(比如调了 wait()、sleep()、JDBC 查询),JVM 会立刻判定“这次等不到”,后续对该锁的所有竞争者基本跳过自旋。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- RUNNABLE → 鼓励自旋,次数可能提升
- 非 RUNNABLE(尤其 WAITING/TIMED_WAITING)→ 快速放弃,直奔 Monitor 阻塞队列
- 这个判断在每次 CAS 失败后立即执行,不依赖用户代码提示
看机器有没有空闲资源
自旋不是孤立行为,它得看 CPU 能不能撑住。JVM 会结合系统负载做协同决策:
- CPU 使用率长期超 90% → 主动压低自旋预算,避免加剧争抢
- 活跃线程数远超 CPU 核心数(比如 > 核心数×2)→ 认为资源饱和,降低自旋意愿
- 单核环境或容器限制为 1vCPU → 自旋自动禁用(因为持有线程根本没机会被调度出去)
- JDK 9+ 还会插入 Thread.onSpinWait() 指令,提示 CPU 进入节能空转模式,减少功耗
调整只发生在轻量级锁竞争失败后的那一小段窗口
自适应自旋不是独立锁类型,而是 synchronized 锁升级路径中的“缓冲动作”:当轻量级锁 CAS 失败后、准备升级为重量级锁前,JVM 插入这段忙等逻辑。整个过程不改对象头锁标志位(仍是“00”),也不创建 Monitor,纯用户态循环。一旦耗尽自旋次数仍失败,才真正替换 Mark Word、进入操作系统级阻塞。
- 它不改变锁本身的状态,只优化获取路径
- 不同锁对象互不影响,A 锁调高了,B 锁照样按自己历史走
- 无法通过 Java 代码干预,也不建议用 -XX:PreBlockSpin 等旧参数强行覆盖
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










