1. 从锁的底层实现看Java对象内存布局
当我们在Java代码中使用synchronized关键字时,JVM会自动处理锁的获取和释放。但你是否想过,为什么synchronized不能直接作用于int、boolean这些基本数据类型?这个看似简单的问题背后,隐藏着Java对象内存模型的精妙设计。
在HotSpot虚拟机中,每个对象都包含一个被称为Mark Word的关键数据结构,它位于对象头(Object Header)中。这个Mark Word就像是对象的"身份证",记录了包括锁状态、GC年龄、哈希码等关键信息。32位JVM中Mark Word占32位,64位JVM中占64位。以下是Mark Word在不同锁状态下的结构示意图:
code复制无锁状态:
| 25bit HashCode | 4bit Age | 1bit(0) | 2bit(01) |
偏向锁:
| 23bit ThreadID | 2bit Epoch | 4bit Age | 1bit(1) | 2bit(01) |
轻量级锁:
| 30bit 指向栈中锁记录的指针 | 2bit(00) |
重量级锁:
| 30bit 指向监视器Monitor的指针 | 2bit(10) |
GC标记:
| 30bit 空 | 2bit(11) |
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 为什么基本数据类型无法加锁
2.1 对象头的关键作用
基本数据类型(如int、char等)直接存储在栈帧的局部变量表中,它们没有对象头这个关键结构。而锁机制依赖的对象监视器(Monitor)必须关联到一个具有对象头的实例上。这就是为什么以下代码会编译错误:
java复制int count = 0;
synchronized(count) { // 编译错误:基本数据类型不能作为锁对象
count++;
}
2.2 锁升级过程中的对象头变化
当使用synchronized锁定对象时,JVM会根据竞争情况自动进行锁升级,这个过程中对象头的Mark Word会经历以下变化:
- 偏向锁:第一个线程访问时,在Mark Word中记录线程ID(23bit)
- 轻量级锁:出现竞争时,Mark Word转换为指向栈中锁记录的指针(30bit)
- 重量级锁:激烈
