JVM对Synchronized的优化
一、是什么?
JVM对synchronized
的优化是一系列降低锁性能开销的技术,通过锁升级机制(偏向锁→轻量级锁→重量级锁)和运行时策略(如锁消除/粗化),在保证线程安全的同时减少同步操作的开销。这些优化主要在JDK 6+中实现。
1. 偏向锁(Biased Locking)
一、是什么?
JDK 6引入,假设锁总被同一线程访问,首次获取时记录线程ID,后续该线程无需加锁操作。
二、解决什么问题
消除无竞争场景下的锁开销(如单线程重复获取锁)。
三、核心流程
- 首次加锁:CAS将线程ID写入对象头
- 重入:检查线程ID匹配直接执行
- 竞争发生:升级为轻量级锁
四、应用场景
单线程重复访问同步块的场景(如循环内的同步)。
五、注意事项 - JDK 15后默认禁用(
-XX:-UseBiasedLocking
),因现代应用多线程竞争激烈 - 可通过
-XX:BiasedLockingStartupDelay=0
关闭延迟启用
2. 轻量级锁(Lightweight Locking)
一、是什么?
通过CAS自旋替代OS互斥锁,线程在栈帧创建锁记录(Lock Record),尝试用CAS将对象头替换为指向锁记录的指针。
二、解决什么问题
降低短时锁竞争场景的线程阻塞开销(如两个线程交替执行)。
三、核心流程
- 拷贝对象头到栈帧(Displaced Mark Word)
- CAS将对象头指向锁记录
- 成功:获得锁;失败:自旋或升级
四、应用场景
低竞争、同步代码执行快的场景(如瞬时状态更新)。
五、注意事项
- 自旋会消耗CPU,超过阈值(
-XX:PreBlockSpin
)升级重量级锁 - 适用于锁持有时间<线程切换耗时的场景
3. 重量级锁(Heavyweight Locking)
一、是什么?
传统互斥锁,通过OS的mutex
实现,未获锁线程进入阻塞队列。
二、解决什么问题
高竞争场景下的线程调度(如百级线程争抢资源)。
三、核心机制
- 对象头指向操作系统互斥量(mutex)
- 竞争线程进入
_cxq
队列等待唤醒
四、应用场景
高并发竞争场景(如秒杀系统核心资源)。
五、注意事项 - 线程阻塞/唤醒涉及内核态切换,开销最大
- JDK 6后通过适应性自旋减少直接阻塞
4. 锁消除(Lock Elimination)
一、是什么?
JIT编译器通过逃逸分析移除不可能存在竞争的锁。
二、解决什么问题
消除无意义的同步开销(如局部对象锁)。
三、触发条件
对象未逃逸出当前线程(如方法内私有锁)。
四、Java示例
java
public void safeMethod() {
// 局部对象不会逃逸,锁被消除
Object localLock = new Object();
synchronized(localLock) {
System.out.println("Lock eliminated");
}
}
五、注意事项
- 依赖JVM逃逸分析(默认开启)
- 需服务器模式运行(
-server
)
5. 锁粗化(Lock Coarsening)
一、是什么?
将相邻的同步块合并为单个更大范围的锁。
二、解决什么问题
减少频繁加锁/解锁的开销(如循环内同步)。
三、Java示例
java
// 优化前:100次加锁
for (int i = 0; i < 100; i++) {
synchronized(lock) { /* 操作 */ }
}
// 优化后:1次加锁
synchronized(lock) {
for (int i = 0; i < 100; i++) { /* 操作 */ }
}
四、应用场景
循环体内同步或连续同步代码块。
其他关键优化
- 适应性自旋(Adaptive Spinning)
根据历史自旋成功率动态调整自旋次数,避免CPU空转。 - 锁膨胀(Inflation)
轻量级锁竞争失败时,通过ObjectMonitor
转为重量级锁。
锁升级流程总结
注意事项
- 锁升级不可逆:重量级锁无法降级
- 性能权衡:
- 低竞争:偏向锁/轻量级锁
- 高竞争:直接重量级锁(避免多次升级开销)
- 监控工具:
- JOL(Java Object Layout)分析对象头
- JFR(Java Flight Recorder)监控锁竞争
总结
优化技术 | 目标场景 | JDK版本 | 性能影响 |
---|---|---|---|
偏向锁 | 单线程重复访问 | 6+ | 无竞争时零开销 |
轻量级锁 | 低竞争短时操作 | 6+ | CAS自旋消耗CPU |
锁消除 | 线程私有锁 | 6+ | 完全消除同步开销 |
锁粗化 | 连续同步块/循环 | 6+ | 减少锁操作次数 |
适应性自旋 | 中等竞争 | 6+ | 减少线程阻塞 |
💡 实践建议:在超高并发场景(如
QPS>10k
),考虑ReentrantLock
替代synchronized
,因其提供更灵活的锁控制(可中断、公平锁、条件变量)。