“轻量级”是相对于使用操作系统互斥量
来实现的传统锁而言的,因此传统的锁机制就称为“重量级”锁。
本意
首先需要强调
一点的是,轻量级锁并不是用来代替
重量级锁的,它的本意
是在没有
多线程竞争的前提
下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。
原理
轻量级锁能提升程序同步性能的依据
是“对于绝大部分的锁,在整个同步周期内都是不存在竞争的
”,这是一个经验数据
。
如果没有竞争,轻量级锁使用CAS操作避免了使用互斥量的开销,但如果存在锁竞争,除了互斥量
的开销外,还额外发生了CAS操作
,因此在有竞争的情况
下,轻量级锁
会比传统的重量级锁更慢
。
过程
加锁
在代码进入同步块的时候,如果此同步对象没有被锁定(锁标志位为“01”状态),虚拟机首先
将在当前线程的栈帧
中建立一个名为锁记录
(LockRecord)的空间,用于存储锁对象
目前的Mark Word
的拷贝
(官方把这份拷贝加了一个Displaced前缀,即DisplacedMarkWord),这时候线程堆栈与对象头的状态如下图所示。

然后,虚拟机将使用CAS操作
尝试将对象的
Mark Word更新
为指向LockRecord
的指针。如果这个更新动作成功了,那么这个线程就拥有
了该对象的锁,并且对象Mark Word的锁标志位
(Mark Word的最后2bit)将转变为“00”,即表示此对象处于轻量级锁定状态,这时候线程堆栈与对象头的状态如下图所示。

如果这个更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是,说明当前线程已经拥有
了这个对象的锁,那就可以直接进入同步块继续执行,否则说明这个锁对象已经被其他线程抢占
了。
解锁
也是通过CAS操作来进行的,如果对象的Mark Word仍然指向着线程
的锁记录
,那就用CAS操作把对象当前的Mark Word和线程中复制的Displaced Mark Word替换回来
。
如果替换成功,整个同步过程就完成了。如果替换失败
,说明有其他线程尝试过
获取该锁,那就要在释放锁的同时
,唤醒
被挂起的线程。

膨胀升级
如果有两条以上
的线程争用同一个锁
,那轻量级锁就不再有效,要膨胀
为重量级锁。
锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)
的指针,后面等待锁的线程也要进入阻塞状态。
网友评论