从厕所门锁到JVM的奇妙旅程
想象一个公共厕所的门锁进化史:
- 普通门锁:每次有人进去就挂"使用中"牌子(基本同步)
- 智能门锁:自动识别VIP用户、记录等待队列、自适应优化(JVM的synchronized优化)
Java的synchronized
关键字就是这样一套不断进化的同步机制,从JDK1.0到现在的Java17,它已经经历了多次革命性升级!
同步的三大实现阶段
1. 重量级锁时代(JDK1.0-1.5)
public synchronized void method() {
// 早期直接关联操作系统mutex
// 用户态→内核态切换,性能杀手!
}
2. 偏向锁/轻量级锁(JDK1.6)
Object lock = new Object();
synchronized(lock) {
// JVM自动选择最优锁策略
// 无竞争→偏向锁 | 轻微竞争→轻量级锁
}
3. 完全自适应的现代锁(JDK15+)
// JVM根据运行情况动态选择:
// - 偏向锁(Biased Locking)
// - 轻量级锁(Thin Lock)
// - 重量级锁(Inflated Monitor)
// - 甚至完全消除锁(锁消除优化)
对象头里的秘密(64位JVM)
|----------------------------------------------------------------------------------|
| Mark Word (64 bits) | Klass Word (64 bits) |
|--------------------------------------|-------------------------------------------|
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |
| | cms_free:1 | |
|--------------------------------------|-------------------------------------------|
- lock标志位:
- 00:轻量级锁
- 01:无锁/偏向锁
- 10:重量级锁
- 11:GC标记
锁的四种状态转换
1. 偏向锁(单线程优化)
// 第一个获取锁的线程会"偏向"这个线程
// 只需在对象头记录线程ID(类似"VIP专属")
2. 轻量级锁(CAS自旋)
// 当有轻微竞争时:
// - 在栈帧创建Lock Record
// - 用CAS尝试获取锁(避免OS调用)
3. 重量级锁(终极方案)
// 竞争激烈时升级为:
// - 通过操作系统的mutex实现
// - 线程进入等待队列
// - 涉及用户态→内核态切换
同步代码的字节码真相
public void test();
Code:
0: aload_0
1: dup
2: astore_1
3: monitorenter // 进入同步块
4: aload_1
5: monitorexit // 正常退出
6: goto 14
9: astore_2
10: aload_1
11: monitorexit // 异常退出
12: aload_2
13: athrow
14: return
5大编译器优化策略
1. 锁消除(Lock Elision)
// JVM发现不可能存在共享竞争时
StringBuffer sb = new StringBuffer(); // 局部变量
sb.append("Hello"); // 自动去除同步操作
2. 锁粗化(Lock Coarsening)
// 连续同步块合并
synchronized(lock) { /* 操作1 */ }
synchronized(lock) { /* 操作2 */ }
// 优化为→
synchronized(lock) { /* 操作1+操作2 */ }
3. 自适应自旋(Adaptive Spinning)
// 根据历史成功率动态调整自旋次数
while (!tryLock()) {
if (自旋超过阈值) {
挂起线程;
break;
}
短暂自旋;
}
4. 偏向锁延迟启用
// 默认延迟4秒后启用偏向锁(-XX:BiasedLockingStartupDelay=4000)
// 避免启动阶段大量锁竞争导致无效偏向
5. 逃逸分析的辅助
// 如果对象不会逃逸当前线程
// 可以完全去掉同步操作
与ReentrantLock的性能对比
场景 | synchronized | ReentrantLock |
---|---|---|
单线程无竞争 | 偏向锁≈0开销 | 仍有CAS开销 |
低竞争 | 轻量级锁(CAS) | CAS竞争 |
高竞争 | 重量级锁(OS调度) | AQS队列+自旋 |
可中断性 | 不支持 | 支持 |
公平性 | 完全非公平 | 可配置 |
最佳实践指南
-
优先使用同步块:
// 优于同步方法(缩小范围) synchronized(lockObj) { // 临界区 }
-
避免锁静态对象:
// 危险!锁住Class对象影响全局 synchronized(MyClass.class) { ... }
-
注意锁顺序(防死锁):
// 所有线程按固定顺序获取锁 synchronized(lockA) { synchronized(lockB) { ... } }
-
监控锁竞争:
# 查看JVM锁情况 jstack <pid> | grep -A 10 "BLOCKED"
未来:Loom项目的颠覆
// 虚拟线程(纤程)下的新同步模式
synchronized(lock) {
// 虚拟线程挂起不会阻塞OS线程!
}
一句话总结
synchronized就像Java世界的"智能变色龙"——从偏向锁的VIP通道到重量级锁的终极方案,它总能找到最高效的同步方式,是现代JVM优化技术的集大成者! 🦎✨