Mark Word
谈JVM的同步机制,不得不先了解一下Java对象头中的Mark Word,JVM同步的实现离不开这个数据结构。
存储内容 | 标记 | 状态 |
hash、对象分代年龄 | 01 | 无锁 |
指向(虚拟机栈中)锁记录地址的指针 | 00 | 轻量级锁 |
指向操作系统重量级锁的指针 | 10 | 重量级锁 |
11 | GC标记 | |
偏向线程ID、对象分代年龄 | 01 | 偏向/可偏向锁 |
锁的类型
重量级锁(heavy-weight locking)
操作系统级别的锁机制,包括互斥(mutex)对象和条件变量(condition variables)
轻量级锁(light-weight locking)
所有现代JVM都引入了经量级锁:
- 避免将每个对象关联操作系统的重量级锁
- 当不存在锁竞争时,使用原子操作来进入退出同步块
- 如果发生锁竞争,回退到操作系统的重量级锁
使用轻量级锁会提高效率,因为大部分锁都不存在竞争。
具体实现
上图描述了标准的加锁过程:
- 开始对象处于未锁定状态。
- 当在对象上进行同步时,mark word被拷贝到当前线程栈帧上的锁记录(lock record,用于追踪被当前线程锁定的对象)中,它被称之为displaced mark。
- 线程使用原子的CAS(Compare-and-swap)指令尝试将mark word的指针指向帧栈上的锁记录。
- 若CAS成功,则该线程获得锁。
- 若CAS失败,则轻量级锁膨胀为重量级锁以管理等待线程。
由于在多处理器上执行原子的CAS操作代价是昂贵的,大多数锁不仅不存在竞争,它们还通常由同一个线程重复加锁/解锁。为了解决这些问题,引入了偏向锁(Biased Locking),只有第一次获取锁时,使用CAS操作将线程ID置于对象头的mark word中 。这个对象会偏向于第一次获取锁的线程,之后该线程锁定和解锁对象不需要任何原子操作或mark word的更新。若另外的一个线程锁定了该对象,则取消偏向。
总结
轻量级锁在使用操作系统重量级锁之前,首先进行CAS操作以减少系统开销,因为大部分锁都是无竞争的。
偏向锁的目的是无竞争的时候连CAS操作都不需要,直接使用偏向线程。