IIWAB

synchronized 锁升级 & JDK15关闭偏向锁

IIWAB 11天前 ⋅ 42 阅读

环境背景:Java对象头Mark Word实现锁状态,锁一共有4种状态:无锁 → 偏向锁 → 轻量级锁 → 重量级锁,只能升级不能降级。JDK1.6引入偏向锁,JDK15默认禁用偏向锁(-XX:+UseBiasedLocking默认false)

一、synchronized 为什么可以锁升级?

锁升级本质:JDK1.6对synchronized做自适应自旋+锁状态分层优化,依据竞争激烈程度,逐步切换锁实现,平衡性能开销。 对象头Mark Word存储锁标记位: |锁状态|标记位|说明| |---|---|---| |无锁|01|没有线程竞争| |偏向锁|01|偏向某一个线程,单线程优先| |轻量级锁|00|自旋,多线程交替访问,竞争不激烈| |重量级锁|10|向OS申请互斥锁,阻塞线程,竞争激烈|

1)偏向锁

场景:大多数情况锁总是被同一个线程获取,几乎没有竞争。

  • 线程第一次获取锁:CAS把Mark Word里线程ID设置为当前线程,标记为偏向锁。不需要每次加锁解锁做CAS操作,后续该线程再次进入同步块只做简单判断,性能极高。
  • 没有真正加锁,只是标记“这个锁归你”。

缺陷:一旦出现另外线程竞争,偏向锁撤销(revoke)代价很大,需要全局安全点STW,暂停持有锁的线程

2)轻量级锁

场景:多线程交替获取锁,竞争不激烈,不会同时抢锁。 当有第二个线程来抢偏向锁,偏向锁撤销,升级为轻量级锁。

  • 线程在栈帧创建锁记录LR,CAS把对象Mark Word替换指向LR;CAS成功拿到锁。
  • 拿不到锁的线程自适应自旋循环,不阻塞,不调用操作系统内核。
  • 自旋有次数限制,不会无限自旋,消耗CPU。

适用:线程短时间交替持有锁。如果两个线程同时抢锁(真竞争),自旋失败,升级重量级锁。

3)重量级锁

场景:多个线程同时竞争锁,竞争激烈。 依赖操作系统mutex互斥锁。

  • 竞争失败的线程直接阻塞,放入等待队列,内核态完成线程挂起唤醒。
  • 用户态→内核态切换,开销大。

✅为什么只能升级,不能降级?

  1. 降级需要STW,开销巨大。已经升级到重量级锁之后,如果再降级回轻量/偏向锁,需要遍历阻塞队列、重置Mark Word,要安全点暂停所有线程,得不偿失。
  2. JVM设计假设:一旦发生激烈竞争,大概率后续竞争依旧激烈,没有必要退回到低层级锁。

重点:重量级锁不会降级;轻量级锁在无竞争时可以回到无锁,但不会回到偏向锁。偏向锁一旦撤销,该对象再也不会进入偏向锁状态。

总结锁升级完整流程: 无锁 → 单线程反复获取 → 偏向锁 出现第二个线程竞争 → 撤销偏向锁 → 轻量级锁 多线程同时争抢,自旋耗尽 → 重量级锁


二、JDK15 默认关闭偏向锁 UseBiasedLocking=false

可以手动开启:-XX:+UseBiasedLocking,但是官方不推荐。

1)偏向锁的核心痛点

偏向锁收益建立在:锁长期被同一个线程占用,几乎没有竞争。 但是它的撤销(revoke)代价极高

  1. 撤销偏向锁必须走到 STW全局安全点,暂停持有锁的线程,修正对象头Mark Word。
  2. 批量重偏向、批量撤销:当大量对象偏向锁被撤销,会触发批量处理;高并发场景下,批量撤销会带来明显的STW停顿。

2)现代业务环境变化

  1. 现在高并发多线程程序越来越多,很多锁会被多个线程访问,真正适合偏向锁的场景变少。很多锁刚偏向,马上就遇到竞争,立刻触发撤销,不仅没有收益,反而引入STW开销。
  2. 轻量级锁的CAS性能已经优化的非常好。对于单线程场景,轻量级锁CAS开销已经很小,偏向锁带来的性能提升微乎其微,但是一旦竞争,惩罚代价很大。

3)官方考量(OpenJDK官方说明)

  1. 偏向锁代码复杂,维护成本高,很多bug都来源于偏向锁逻辑。
  2. 真实业务中,偏向锁的性能收益经常被它撤销带来的STW抵消甚至反亏。
  3. 保留开关,不删除该特性,如果你的业务确定大量锁始终只有一个线程访问,可以手动打开。

JDK15变更:只是默认关闭,不是移除偏向锁

4)补充对比

  • JDK1.6~14:UseBiasedLocking=true 默认开启
  • JDK15+:UseBiasedLocking=false 默认关闭

关闭偏向锁之后锁路径:无锁 → 轻量级锁 → 重量级锁,跳过偏向锁这一步。

三、常见面试追问

  1. Q:关闭偏向锁之后,单线程拿synchronized会不会变慢? A:会走轻量级锁CAS。现代CPU CAS指令极快,绝大多数场景性能差距很小,避免了偏向锁撤销STW风险。

  2. Q:偏向锁一定比轻量级锁快? A:只有完全无竞争,同一个线程反复拿锁才快。一旦出现竞争,撤销的STW开销会远大于收益。

  3. Q:锁可以降级吗? A:重量级锁不会降级;轻量级锁解锁后回到无锁,但不会回到偏向锁。偏向锁撤销后对象永久不再偏向。

  4. Q:什么是批量重偏向、批量撤销?

当一个类大量对象发生偏向锁撤销,JVM判断:可能是线程换了,就做批量重偏向,把这些对象偏向新线程; 如果撤销继续增多,触发批量撤销,这个类所有新创建对象直接禁止偏向。


全部评论: 0

    我有话说: