在讨论synchronized锁升级和Mark Word时,提到的"对象"通常指的是锁对象,也就是被用作synchronized同步锁的那个Java对象。
1. 什么是锁对象?
锁对象是指被用于synchronized同步代码块或方法的对象实例。例如:
// 这个lock就是锁对象
Object lock = new Object();
synchronized(lock) {
// 同步代码块
}
或者:
// 这个实例对象本身就是锁对象
public synchronized void method() {
// 同步方法
}
2. 为什么锁对象特别重要?
因为Java中的synchronized机制是通过锁对象的对象头中的Mark Word来实现的:
- 当线程进入synchronized块时,JVM会修改锁对象的Mark Word
- 锁状态(偏向锁、轻量级锁、重量级锁)都记录在锁对象的Mark Word中
- 锁升级过程就是锁对象的Mark Word内容变化的过程
3. 普通对象与锁对象的区别
所有Java对象在内存中都有对象头和Mark Word,但:
- 普通对象:Mark Word主要存储哈希码、GC分代年龄等信息
- 锁对象:当对象被用作同步锁时,其Mark Word会被JVM特殊处理,记录锁状态和线程信息
4. 对象如何变成锁对象?
对象成为锁对象是动态的:
- 初始状态:任何新创建的对象都是普通对象
- 首次同步:当对象第一次被用于synchronized时,JVM会将其标记为锁对象
- 状态变化:根据线程竞争情况,锁对象的Mark Word会经历锁升级过程
5. 锁对象的生命周期示例
Object obj = new Object(); // 1. 普通对象,Mark Word存储哈希码等
synchronized(obj) { // 2. 首次同步,变为偏向锁(如果开启偏向锁)
// ...
}
// 多线程竞争时
new Thread(() -> {
synchronized(obj) { // 3. 可能升级为轻量级锁或重量级锁
// ...
}
}).start();
6. 如何验证锁对象的变化?
可以使用JOL工具观察锁对象Mark Word的变化:
Object lock = new Object();
System.out.println("初始状态:");
System.out.println(ClassLayout.parseInstance(lock).toPrintable());
synchronized(lock) {
System.out.println("第一次加锁:");
System.out.println(ClassLayout.parseInstance(lock).toPrintable());
}
// 多线程竞争观察升级
new Thread(() -> {
synchronized(lock) {
System.out.println("竞争加锁:");
System.out.println(ClassLayout.parseInstance(lock).toPrintable());
}
}).start();
7. 特殊情况
- Class对象:每个类的Class对象也可以作为锁对象
- 数组对象:数组也可以作为锁对象
- 字符串常量:由于字符串驻留机制,要谨慎用作锁对象
总结
在synchronized锁升级机制中,"对象"指被用作同步锁的那个对象实例(锁对象)。锁对象的Mark Word是JVM实现锁状态记录和锁升级的核心数据结构,理解这一点对掌握Java并发机制至关重要。