什么是企业营销网站,开发区二手房房价最新信息,学做网站需要什么,动易网站模版的制作Java 虚拟机#xff08;JVM#xff09;中的锁升级机制#xff08;也称为锁膨胀#xff09;是 HotSpot 虚拟机为了优化 synchronized 关键字的性能而引入的一项重要技术。它的核心思想是#xff1a;根据实际遇到的竞争激烈程度#xff0c;动态地将锁从开销最小的状态逐步升… Java 虚拟机JVM中的锁升级机制也称为锁膨胀是 HotSpot 虚拟机为了优化 synchronized 关键字的性能而引入的一项重要技术。它的核心思想是根据实际遇到的竞争激烈程度动态地将锁从开销最小的状态逐步升级到开销更大的状态从而在无竞争或低竞争时减少锁操作的开销而在高竞争时保证必要的互斥性和线程调度能力。
锁的状态主要有四种升级路径如下
无锁 - 偏向锁 - 轻量级锁 - 重量级锁
锁只能升级膨胀不能降级虽然理论上重量级锁在竞争消失后可以降级但HotSpot 实现中为了简化很少进行降级尤其是从重量级锁降级。
1. 无锁状态
初始状态 当一个对象刚被创建出来且没有任何线程尝试获取它的锁时它就处于无锁状态。特点 没有锁的开销。适用场景 对象从未被同步访问或只被单个线程访问无需同步。
2. 偏向锁
设计目标 优化同一个线程重复进入同步块的场景无实际竞争。消除在无竞争情况下的同步原语开销如 CAS。工作原理 当第一个线程T1访问同步块时JVM 会检查对象头中的 Mark Word。如果当前是无锁状态JVM 使用 CAS 操作尝试将 Mark Word 中的线程 ID 设置为 T1 的 ID并将锁标志位设置为偏向模式。如果 CAS 成功T1 就持有了该对象的偏向锁。后续只要 T1 进入这个同步块无需再进行任何同步操作如 CAS 或锁申请只需简单检查对象头中的线程 ID 是否还是自己。 升级触发 竞争出现 当另一个线程T2尝试获取这个已经被偏向于 T1 的锁时偏向锁就会失效。JVM 会撤销偏向锁。撤销过程需要等待持有偏向锁的线程T1到达全局安全点Safepoint暂停 T1。检查 T1 的状态 如果 T1 已经退出同步块不再持有锁则将对象头设置为无锁状态或者根据情况尝试重新偏向给 T2。如果 T1 仍在同步块中则将锁升级为轻量级锁。JVM 会在 T1 的栈帧中创建一个锁记录Lock Record并将对象头的 Mark Word 复制到该锁记录中称为 Displaced Mark Word然后用 CAS 操作将对象头指向 T1 栈帧中的锁记录地址轻量级锁状态。 特点 适用于只有一个线程反复访问同步块的场景。加锁解锁几乎无额外开销。撤销偏向锁有代价需要暂停线程。关闭偏向锁 由于偏向锁在存在竞争时撤销有开销且现代应用中共享数据竞争往往更常见从 JDK 15 开始偏向锁默认被禁用可通过 -XX:UseBiasedLocking 开启但已不推荐。
3. 轻量级锁
设计目标 优化多个线程交替执行同步块但未发生真正并发竞争的场景低竞争。避免直接使用重量级锁带来的操作系统内核态切换的开销。工作原理 当线程尝试获取轻量级锁时可能是从无锁升级而来也可能是从偏向锁撤销升级而来JVM 会在当前线程的栈帧中创建一个锁记录空间。将对象头的 Mark Word 复制到该锁记录中称为 Displaced Mark Word。然后线程尝试使用 CAS 操作将对象头中的 Mark Word 替换为指向该锁记录的指针。 如果 CAS 成功当前线程获得轻量级锁。锁标志位变为 00。如果 CAS 失败说明对象头已被其他线程修改即发生了竞争当前线程会自旋循环尝试 CAS一小段时间自适应自旋。 如果在自旋期间成功获取到锁则继续执行。如果自旋结束仍未成功或者自旋过程中竞争加剧如又有新线程加入竞争则锁升级为重量级锁。 解锁过程 使用 CAS 操作将 Displaced Mark Word 替换回对象头。 如果成功解锁完成。如果失败说明锁已经膨胀为重量级锁则在释放锁的同时唤醒等待线程。 特点 使用 CAS 和自旋代替互斥量避免了用户态到内核态的切换适用于线程阻塞时间非常短的场景“忙等”。自旋会消耗 CPU。如果锁持有时间较长或竞争激烈自旋会浪费 CPU性能反而下降。升级触发 CAS 失败且自旋获取锁失败或自适应策略判断竞争激烈、调用 wait() 方法因为 wait() 需要重量级锁的监视器模型支持。
4. 重量级锁
设计目标 处理高并发、激烈竞争的场景。保证在任意时刻只有一个线程能进入同步块。工作原理 当锁升级到重量级锁时对象头中的 Mark Word 会指向一个与对象关联的监视器锁Monitor也称为管程或互斥锁这个结构通常存在于堆中。该 Monitor 内部维护了一个入口队列Entry Set 和一个等待队列Wait Set。当一个线程尝试获取重量级锁时 如果锁可用未被持有则获取成功成为锁的持有者。如果锁已被其他线程持有则当前线程会被阻塞Park并被操作系统挂起放入入口队列等待唤醒。这涉及到用户态到内核态的切换线程上下文切换开销最大。 当持有锁的线程释放锁时它会唤醒入口队列中的某个或所有等待线程具体策略取决于实现如公平/非公平被唤醒的线程会重新尝试获取锁。 特点 真正的互斥锁。阻塞线程不消耗 CPU 空转。适用于竞争激烈或临界区执行时间较长的场景。线程阻塞、唤醒、上下文切换开销很大。升级触发 轻量级锁自旋失败、调用 Object.wait() 方法强制升级。
总结锁升级机制
锁状态目标场景核心机制优点缺点升级触发条件无锁无同步访问-无开销无法提供线程安全首次线程访问偏向锁单线程重复访问CAS 设置 Thread ID同一线程后续进入无开销竞争时撤销开销大需暂停线程第二个线程尝试获取锁轻量级锁低竞争交替执行CAS 自旋 (栈锁记录)避免内核切换开销小自旋消耗 CPU长时间竞争性能下降CAS失败且自旋失败 / 调用 wait()重量级锁高竞争操作系统 Monitor阻塞线程不消耗 CPU 空转阻塞/唤醒开销大内核切换轻量级锁升级失败 / 显式调用 wait()
关键点
自适应自旋 轻量级锁中的自旋次数不是固定的JVM 会根据之前在该锁上的自旋成功情况以及持有者的状态动态调整自旋时间适应性自旋。锁消除 JIT 编译器在运行时通过逃逸分析如果发现某个锁对象不可能被其他线程访问到即不会发生竞争它会将这个锁操作完全消除掉。锁粗化 如果 JVM 检测到有一连串连续的操作都对同一个对象反复加锁和解锁即使是在循环中它可能会将加锁的范围扩大粗化到整个操作序列的外部从而减少不必要的锁申请/释放次数。偏向锁的争议与默认关闭 在现代多核、高并发环境下共享数据的竞争是常态偏向锁在首次获得和撤销时的开销以及它对应用程序启动性能的影响大量类初始化时偏向锁操作变得不可忽视。自 JDK 15 起偏向锁默认被禁用轻量级锁成为更常见的起点。-XX:-UseBiasedLocking 可以显式关闭它在 JDK 15 已经是默认行为。hashCode() 的影响 当调用一个未被覆盖的 Object.hashCode() 或 System.identityHashCode() 时如果对象处于偏向锁或轻量级锁状态会导致锁撤销或升级因为无锁状态下的 Mark Word 需要存储哈希码。
理解锁升级的意义
锁升级机制体现了 JVM 在性能与正确性之间所做的精妙平衡
无竞争/低竞争 最大程度减少开销偏向锁、轻量级锁。高竞争 保证正确性和线程调度的公平性/效率接受较大的开销重量级锁。
了解锁升级机制对于编写高效、正确的并发程序至关重要。它解释了为什么简单的 synchronized 在低竞争场景下性能可以非常好而在高竞争场景下性能会显著下降。在需要极高并发性能的场景下开发者可能会选择更灵活、可优化的显式锁如 ReentrantLock但 synchronized 结合锁升级在大多数场景下已经是非常高效且简洁的选择。