Java轻量级锁原理详解(Lightweight Locking)

你们知道,Java的多线程安全是基于Lock机制实现的,而Lock的性能每每不如人意。
缘由是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖操做系统互斥(mutex)来实现的。
互斥是一种会致使线程挂起,并在较短的时间内又须要从新调度回原线程的,较为消耗资源的操做。 安全

为了优化Java的Lock机制,从Java6开始引入了轻量级锁的概念。 多线程

轻量级锁(Lightweight Locking)本意是为了减小多线程进入互斥的概率,并非要替代互斥。
它利用了CPU原语Compare-And-Swap(CAS,汇编指令CMPXCHG),尝试在进入互斥前,进行补救。 性能

本文将详细介绍JVM如何利用CAS,实现轻量级锁。 优化

 

原理详解

Java Object Model中定义,Object Header是一个2字(1 word = 4 byte)长度的存储区域。
第一个字长度的区域用来标记同步,GC以及hash code等,官方称之为 mark word。第二个字长度的区域是指向到对象的Class。 spa

在2个word中,mark word是轻量级锁实现的关键。它的结构见下表 操作系统

从表中能够看到,state为lightweight locked的那行即为轻量级锁标记。bitfieds名为指向lock record的指针,这里的lock record,实际上是一块分配在线程堆栈上的空间区域
用于CAS前,拷贝object上的mark word(为何要拷贝,请看下文)。
第三项是重量级锁标记。后面的状态单词颇有趣,inflated,译为膨胀,在这里意思实际上是锁已升级到OS-level。
在本文的范围内,咱们只关注第二和第三项便可。 线程

为了能直观的理解lock,unlock与mark word之间的联系,我画了一张流程图: 指针

在图中,提到了拷贝object mark word,因为脱离了原始mark word,官方将它冠以displaced前缀,即displaced mark word(置换标记字)。
这个displaced mark word是整个轻量级锁实现的关键,在CAS中的compare就须要用它做为条件。 code

为何要拷贝mark word?
其实很简单,缘由是为了避免想在lock与unlock这种底层操做上再加同步。 对象

在拷贝完object mark word以后,JVM作了一步交换指针的操做,即流程中第一个橙色矩形框内容所述。
将object mark word里的轻量级锁指针指向lock record所在的stack指针,做用是让其余线程知道,该object monitor已被占用。
lock record里的owner指针指向object mark word的做用是为了在接下里的运行过程当中,识别哪一个对象被锁住了。

下图直观地描述了交换指针的操做。

exchange_pointer_1

最后一步unlock中,咱们发现,JVM一样使用了CAS来验证object mark word在持有锁到释放锁之间,有无被其余线程访问。 若是其余线程在持有锁这段时间里,尝试获取过锁,则可能自身被挂起,而mark word的重量级锁指针也会被相应修改。 此时,unlock后就须要唤醒被挂起的线程。