偏向锁:原理、优化与高频面试题解析

偏向锁:原理、优化与高频面试题解析
一、什么是偏向锁偏向锁Biased Locking是 Java 6 引入的一项锁优化技术其核心思想是在无竞争的情况下消除整个同步原语的开销。简单比喻一把锁默认“偏袒”第一个访问它的线程。只要这个线程后续再来无需任何原子操作如 CAS即可直接进入同步块就像回自己家一样方便。二、为什么需要偏向锁在早期 synchronized 实现中即使没有线程竞争每次加锁/解锁也需要执行 CAS 操作更新对象头中的锁标志这在高频单线程访问同步块的场景下如某些工具类、缓存会产生不必要的性能损耗。偏向锁的目标将“锁属于某个线程”这个状态记录在对象头里让该线程后续访问时实现“零成本”加锁。三、偏向锁的工作原理三步曲1. 加锁流程步骤一检查线程访问同步块时先检查对象头中的偏向锁标志和线程ID。如果标志为1且线程ID指向自己 → 直接进入同步块零成本。如果标志为1但线程ID是别人 → 进入锁升级流程。如果标志为0无锁状态→ 尝试通过 CAS 将对象头中的线程ID设为当前线程ID并设置偏向模式。步骤二撤销当有第二个线程尝试获取锁时JVM 需要撤销偏向锁。这个过程需要暂停持有偏向锁的线程Stop The World检查其是否仍在同步块中。若已退出 → 将对象头恢复为无锁状态然后两个线程竞争升级为轻量级锁。若未退出 → 直接升级为轻量级锁。步骤三升级撤销后锁状态变为轻量级锁后续竞争通过 CAS 自旋进行。2. 对象头变化64位虚拟机为例无锁状态 Mark Word (64 bits): [unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:0 | lock:01] 偏向锁状态线程A持有 Mark Word: [thread_id:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:01] 轻量级锁状态 Mark Word: [ptr_to_lock_record:62 | lock:00] 重量级锁状态 Mark Word: [ptr_to_monitor:62 | lock:10]四、偏向锁的“尴尬”与优化演变1. 偏向锁的代价撤销成本高需要 STW如果程序中存在大量锁竞争频繁撤销反而降低性能。适用场景窄真正适合“一直只有一个线程访问”的场景并不多。2. JDK 15 及之后的变革JDK 15 开始默认禁用偏向锁-XX:-UseBiasedLocking。JDK 18 正式废弃偏向锁相关代码。原因现代硬件上 CAS 操作已非常高效而偏向锁的撤销成本与其收益相比不再划算。维护其复杂性的性价比变低。五、高频面试题与回答要点面试题1说说你对偏向锁的理解回答要点定义一种消除无竞争情况下同步原语的优化。核心“偏袒”第一个获得锁的线程记录其ID该线程后续访问无需原子操作。对象头通过 Mark Word 中的偏向锁标志和线程ID实现。生命周期加锁偏向→ 出现竞争 → 撤销可能STW→ 升级为轻量级锁。现状JDK 15 默认禁用JDK 18 废弃因撤销成本高且适用场景少。面试题2偏向锁、轻量级锁、重量级锁有什么区别回答要点synchronized 锁的四种状态锁升级过程是无锁 → 偏向锁 → 轻量级锁 → 重量级锁。它们的主要区别如下锁类型适用场景实现机制开销升级条件无锁无竞争或使用 CAS 等乐观锁对象头 Mark Word 记录 hashcode、分代年龄等无同步开销线程尝试获取锁时进入偏向锁或轻量级锁偏向锁只有一个线程访问同步块对象头记录线程ID该线程后续访问无需 CAS无竞争时近乎零开销第二个线程尝试获取锁轻量级锁线程交替执行竞争轻微CAS 自旋栈中创建 Lock Record 拷贝对象头少量 CAS 自旋开销自旋失败或第三个线程竞争重量级锁多线程激烈竞争操作系统互斥量mutex线程进入阻塞队列上下文切换开销大轻量级锁自旋失败补充解释无锁对象刚创建时的默认状态或通过 CAS 实现的无锁编程。此时 Mark Word 存储对象的哈希码、分代年龄等信息。偏向锁在“只有一个线程使用锁”的场景下JVM 通过 CAS 将线程 ID 记录到对象头该线程后续进入同步块无需任何原子操作实现“零成本”加锁。轻量级锁当有轻微竞争线程交替执行时JVM 会在当前线程栈帧中创建 Lock Record并尝试通过 CAS 将对象头指向该记录。如果成功则获得锁如果失败则自旋重试。重量级锁当竞争激烈自旋失败时锁升级为重量级锁。此时对象头指向操作系统级别的互斥量monitor未获得锁的线程会进入阻塞队列由操作系统进行调度涉及用户态到内核态的切换开销最大。锁升级方向无锁 → 偏向锁 → 轻量级锁 → 重量级锁不可逆。JDK 6 之后默认开启锁升级优化目的是在保证线程安全的前提下尽量减少同步开销。锁类型适用场景实现机制开销升级条件偏向锁只有一个线程访问同步块对象头记录线程ID无竞争时近乎零开销第二个线程尝试获取轻量级锁线程交替执行竞争轻微CAS自旋栈中Lock Record少量CAS自旋开销自旋失败或第三个线程竞争重量级锁多线程激烈竞争操作系统互斥量线程阻塞排队上下文切换开销大轻量级锁自旋失败面试题3偏向锁为什么会被废弃标准答案撤销成本高需要 STW影响吞吐量。收益下降现代 CPU 的 CAS 操作很快无竞争下的性能优势不再明显。复杂度高维护偏向锁状态机增加了 JVM 的复杂性。实际场景少大多数应用锁竞争频繁偏向锁反而成为负担。面试题4如何手动开启/关闭偏向锁# JDK 15 之前 -XX:UseBiasedLocking # 开启默认 -XX:-UseBiasedLocking # 关闭 JDK 15 及之后 -XX:UseBiasedLocking # 需显式开启但已不推荐六、总结偏向锁是什么一项针对“单线程重复访问同步块”的优化通过记录线程ID实现零成本加锁。核心价值在特定场景下消除同步开销。主要问题撤销需要 STW竞争频繁时性能反而下降。历史地位是 JVM 锁优化演进中的重要一环但其设计假设锁多数时间不竞争与现代高并发应用不符故被废弃。学习意义理解偏向锁有助于掌握 JVM 同步优化的设计思想与权衡之道是面试中考察对底层机制理解深度的经典题目。