轻量级锁
轻量级锁是JDK6
时加入的新型锁机制,“轻量级”是相对于使用操作系统互斥量来实现
的传统锁而言的,因此传统的锁机制就被称为“重量级”锁。
轻量级锁的原理
HotSpot
虚拟机对象头
对象头(Object Header
)分为两部分,
第一部分用于存储对象自身的运行时数据,如哈希吗(HashCode
)、GC
分代年龄
(Generational GC Age
)等。这部分数据在长度为32位和64为的Java
虚拟机中
分别会占用32个或64个比特,官方称它为“Mark Word
”。这部分是实现轻量级锁和
偏向锁的关键。
另一部分用于存储指向方法区对象类型数据的指针,如果是数组对象,还会有一个
额外的部分用于存储数组长度。(见对象内存布局)
轻量级锁的工作过程
在代码即将进入同步块的时候,如果此时同步对象没有被锁定(锁标志位为“01”状态),
虚拟机首先将在当前线程的栈桢中建立一个名为锁记录(Lock Record
)的空间,用于
存储锁对象目前的Mark Word
的拷贝(加了一个Displaced
前缀 )。
然后虚拟机将使用CAS
操作尝试把对象的Mark Word
更新为指向Lock Record
的指针。
如果这个更新动作成功了,即代表该线程拥有了这个对象的锁,并且对象Mark Word
的锁标志位(Mark Word
的最后两个比特)转变为“00”,表示此对象处于轻量级状态。
如果这个更新操作失败了,那就意味着至少存在一条线程与当前线程竞争获取该对象的锁。
虚拟机首先会检查对象的Mark Word
是否指向当前线程的栈桢,如果是,说明当前线程
已经拥有了这个对象的锁,那直接进入同步块继续执行就可以,否则就说明这个锁对象
已经被其他线程抢占了。如果出现两条以上线程争用同一个锁的情况,那轻量级锁就不再
有效,必须要膨胀为重量级锁,锁标识的状态边为“10”。
解锁过程同样是通过CAS
操作进行的,如果对象的Mark Word
仍然指向线程的锁记录,
那就用CAS
操作把对象当前的Mark Word
和线程中复制的Displaced Mark Word
替换回来,
如果替换成功,那整个同步过程就顺利完成了;如果替换失败,则说明有其他线程尝试
过获取该锁,就要在释放锁的同时唤醒被挂起的线程。
轻量级锁能够提升程序同步性能的依据是“对于绝大部分的锁,在整个同步周期内
都是不存在竞争的”这一经验法则。如果没有竞争,轻量级锁便通过
CAS
操作成功避免了使用互斥量的开销。但如果确实存在锁竞争,除了互斥量的本身开销外,
还额外发生了
CAS
操作的开销,因此在有竞争的情况下,轻量级锁反而会比传统的重量级锁更慢。