Java偏向锁和轻量级锁由JVM自动启用与升级,目标是减少线程竞争时的内核态切换开销;需通过JVM参数配置、单线程访问模式、短临界区、细粒度锁等手段优化其应用,JDK15+已废弃偏向锁。

Java 中偏向锁和轻量级锁本身不是通过代码显式调用的,而是 JVM 在运行时根据对象访问模式自动启用和升级的优化机制。它们的核心目标就是减少线程竞争时频繁进入内核态(即避免操作系统级阻塞),从而降低上下文切换开销。要“使用”它们,关键在于理解其触发条件,并配合合理的编程习惯与 JVM 参数配置。
一、确保偏向锁和轻量级锁默认启用(JVM 启动参数)
JDK 6 及之后版本默认开启这两项优化,但某些场景(如容器环境、短生命周期应用)可能被禁用:
- ✅ 偏向锁默认开启:
-XX:+UseBiasedLocking(JDK 15 之前有效;注意:JDK 15 起已默认禁用,JDK 17+ 完全移除) - ✅ 轻量级锁默认启用:无需额外参数,只要偏向锁未被禁用或已撤销,轻量级锁会自然介入
- ⚠️ 若需强制启用(适用于 JDK 8–14):
-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
StartupDelay=0避免 JVM 启动后几秒才启用偏向锁,让锁优化更早生效。
? 提示:JDK 15+ 已废弃偏向锁(
-XX:+UseBiasedLocking无效),轻量级锁仍保留。当前(2026年)主流生产环境多用 JDK 17/21,应以轻量级锁 + 自旋 + 锁粗化等策略为主。
二、创造适合偏向锁的场景(单线程反复获取同一把锁)
偏向锁只在无竞争前提下生效,典型适用场景:
- 对象由单一线程长期持有并多次加锁(如线程私有缓存、单例工具类内部同步块)
- 同步方法/代码块作用于同一个对象实例,且极少被其他线程争抢
✅ 好例子:
public class Counter {
private final Object lock = new Object();
private int count = 0;
public void increment() {
synchronized (lock) { // 同一个 lock 实例,且多数由同一线程调用
count++;
}
}
}
若 increment() 总由 Thread-A 调用,JVM 很可能将 lock 对象升级为偏向锁(记录 Thread-A ID),后续加锁只需比对线程 ID,不走 CAS,完全用户态完成。
❌ 破坏偏向锁的行为:
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- 多个线程几乎同时尝试
synchronized(lock) - 锁对象被频繁用于不同线程(如池化对象、共享缓存 key)
- 调用
Object.wait()/notify()—— 这会直接撤销偏向锁
三、控制竞争强度,让轻量级锁“稳住”,避免升级到重量级锁
轻量级锁靠 CAS 尝试抢占,失败则自旋重试;自旋失败(超过阈值,默认 10 次)→ 升级为重量级锁 → 进入内核态阻塞。
✅ 降低升级概率的方法:
-
缩短临界区执行时间
synchronized(obj) { // ✅ 快速操作:仅修改字段、简单计算 value = computeFast(); flag = true; // ❌ 避免:IO、sleep、远程调用、复杂循环 } 减少锁粒度,避免“大锁”
不要用一个全局锁保护多个无关字段,拆成细粒度锁(如map.get(key).lock或ReentrantLock分段)利用锁消除(JIT 优化)和锁粗化
JIT 编译器可能在逃逸分析后直接去掉无竞争的synchronized(锁消除);或合并相邻同步块(锁粗化),减少 CAS 次数避免锁对象被 GC 或频繁创建
新建对象(如new Object())初始为无锁状态,但若生命周期短、竞争少,JVM 可能来不及升级就回收;稳定复用的对象更易进入偏向/轻量状态
四、验证锁状态(辅助调试,非运行必需)
可通过 JOL(Java Object Layout)工具观察对象头 Mark Word:
<!-- Maven 依赖 --> <dependency><groupid>org.openjdk.jol</groupid><artifactid>jol-core</artifactid><version>0.17</version></dependency>
Object obj = new Object(); System.out.println(ClassLayout.parseInstance(obj).toPrintable()); // 输出类似:xxxxxx xx... [biased_lock:0, lock:01] → 无锁 // 加锁后再次打印,可看到 thread_id / lock=00 / lock=10 等变化
也可用 JVM 参数输出锁统计:
-XX:+PrintGCApplicationStoppedTime -XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1
(注:偏向锁撤销会触发 safepoint,大量撤销说明设计不合理)
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










