大家知道,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。
在 2 个 word 中,mark word 是轻量级锁实现的关键。它的结构见下表
从表中可以看到,state 为 lightweight locked 的那行即为轻量级锁标记。bitfieds 名为指向
lock record 的指针,这里的 lock record,其实是一块分配在线程堆栈上的空间区域。
用于 CAS 前,拷贝 object 上的 mark word(为什么要拷贝,请看下文)。
第三项是重量级锁标记。后面的状态单词很有趣, inflated,译为膨胀,在这里意思其实是
锁已升级到 OS-level。
在本文的范围内,我们只关注第二和第三项即可。
为了能直观的理解 lock,unlock 与 mark word 之间的联系,我画了一张流程图: