最近有个三年左右的兄弟面试java 被问到这样一道经典的八股文面试题: 你讲讲java里面的锁升级? 他感觉回答的不是很好,然后回去找资料学习了一波,然后下面是他输出的文章,希望对找工作的其他朋友也有些帮助。
Java 的锁升级机制是 JVM 在 JDK 1.6 后引入的重要优化策略,目的是在多线程环境下平衡 线程安全 与 性能开销。通过动态调整锁的复杂度,JVM 根据竞争强度逐步升级锁的状态,避免在低竞争场景下使用高成本的重量级锁。
锁类型 | 适用场景 | 性能开销 | 核心机制 |
---|---|---|---|
无锁(Unlocked) | 无线程竞争 | 极低 | 直接通过 CAS 操作尝试获取锁。 |
偏向锁(Biased Locking) | 单线程重复访问(无竞争) | 极低 | 对象头记录偏向线程 ID,后续同一线程无需竞争,直接获取锁。 |
轻量级锁(Lightweight Lock) | 低竞争(多个线程交替访问) | 中等 | 通过 CAS 自旋尝试获取锁,避免操作系统级别的阻塞。 |
重量级锁(Heavyweight Lock) | 高竞争(长时间阻塞或高并发) | 高 | 依赖操作系统互斥量(Mutex),线程被挂起并排队等待。 |
锁升级路径为:无锁 → 偏向锁 → 轻量级锁 → 重量级锁,且 不可逆(只能升级,不能降级)。
Mark Word
标记为偏向锁。-XX:PreBlockSpin
调整)。ObjectMonitor
等待唤醒。// 不推荐:锁持有时间过长
synchronized (lock) {
// 复杂计算或 IO 操作
}
// 推荐:仅在关键代码块加锁
int
result
= doSomeComputation(); // 非同步操作
synchronized (lock) {
sharedVariable = result;
}
ConcurrentHashMap
使用分段锁(JDK 8 后改为 CAS + synchronized)。-XX:-UseBiasedLocking # 禁用偏向锁
-XX:PreBlockSpin=5 # 设置自旋次数为 5
public classLockUpgradeExample {
privatefinalObjectlock=newObject();
publicvoidperformTask() {
synchronized (lock) {
// 同步代码块
}
}
publicstaticvoidmain(String[] args) {
LockUpgradeExampleexample=newLockUpgradeExample();
Threadt1=newThread(example::performTask);
Threadt2=newThread(example::performTask);
t1.start(); // 初始为偏向锁(t1)
t2.start(); // 触发偏向锁撤销,升级为轻量级锁
}
}
参数 | 作用 |
---|---|
-XX:+UseBiasedLocking | 开启/关闭偏向锁(默认开启,Java 15+ 默认关闭)。 |
-XX:BiasedLockingStartupDelay=0 | 立即启用偏向锁(避免延迟)。 |
-XX:PreBlockSpin | 设置轻量级锁自旋次数(默认 10)。 |
-XX:-UseSpinning | 关闭自旋锁(强制进入重量级锁)。 |
通过合理设计代码(如减少锁粒度、避免过早膨胀到重量级锁),可以最大化 Java 的并发性能。