前言
线程并发系列文章:
上篇文章分析了偏向锁、轻量级锁的演变过程,本篇将分析重头戏:重量级锁的原理。
通过本篇文章,你将了解到:
1、ObjectMonitor 的运用
2、锁的膨胀过程
3、重量级锁的加锁流程
4、重量级锁的解锁流程
5、重量级锁小结
6、与偏向锁、轻量级锁的比对
1、ObjectMonitor 的运用
我们知道当锁处在轻量级锁的状态时,Mark Word 存放着指向Lock Record指针,Lock Record是线程私有的。
而处在重量级锁状态时说明有线程没拿到锁需要阻塞等待锁,当拥有锁的线程释放锁后唤醒它继续竞争锁。此处就引入了一个问题:其它线程如何找到被阻塞的线程?我们很容易想到:把阻塞的线程放到多线程共享的(能访问)的列表里。
而Lock Record是线程私有的,显然不能满足需求。
因此,重量级锁引入了ObjectMonitor类。
image.png
如上图,Mark Word 存放着指向ObjectMonitor的指针,ObjectMonitor是线程间共享的并且拥有比Lock Record更多的信息。
来看看ObjectMonitor 记录的信息:
#ObjectMonitor.hpp
ObjectMonitor() {
//记录无锁状态的Mark Word
_header = NULL;
_count = 0;
//等待锁的线程个数
_waiters = 0,
//线程重入次数
_recursions = 0;
//指向的对象头
_object = NULL;
//锁的本身,指向线程或者Lock Record
_owner = NULL;
//调用wait()方法后等待锁的队列
_WaitSet = NULL;
//等待队列的锁
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
//ObjectWaiter 队列
_cxq = NULL ;
FreeNext = NULL ;
//ObjectWaiter 队列
_EntryList = NULL ;
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}
可以看出,Lock Record里拥有的信息ObjectMonitor里也有,如存储Mark Word的_header字段,存储指向对象头的指针_object字段。当然,ObjectMonitor还有更丰富的信息,如获取锁失败存放阻塞线程的队列_cxq,调用wait()方法后等待的线程队列_WaitSet等。
2、锁的膨胀过程
知道有ObjectMonitor这个东西了,接下来看看如何使用它。
回顾之前的分析,偏向锁升级为轻量级锁时要修改Mark Word,使之指向Lock Record,轻量级锁升级为重量级锁时也需要修改Mark Word,使之指向ObjectMonitor。
而创建/获取ObjectMonitor 对象的过程即是锁的膨胀过程。
源码里的膨胀过程就是个inflate(xx)函数:
#synchronizer.cpp
ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
...
//死循环,直到获取到ObjectMonitor为止
for (;;) {
//取出Mark Word
const markOop mark = object->mark() ;
//如果是重量级锁
if (mark->has_monitor()) {
//是重量级锁,说明肯定已经有现成的ObjectMonitor,直接用就好了
ObjectMonitor * inf = mark->monitor() ;
return inf ;
}
//正在膨胀的时候
if (mark == markOopDesc::INFLATING()) {
//继续循环,需要等待膨胀完成
continue ;
}
//如果当前是轻量级锁
if (mark->has_locker()) {
//分配ObjectMonitor对象
ObjectMonitor * m = omAlloc (Self) ;
//初始化一些参数
m->Recycle();
m->_Responsible = NULL ;
m->OwnerIsThread = 0 ;
m->_recursions = 0 ;
m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ; // Consider: maintain by type/class
//尝试将Mark Word更改为膨胀状态,此时Mark Word 全是0 --------->(1)
//可能会有多线程走到这,因此用CAS
markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ;
if (cmp != mark) {
//修改失败,继续循环
omRelease (Self, m, true) ;
continue ; // Interference -- just retry
}
//若是修改成功,则取出之前轻量级锁存储的Mark Word
markOop dmw = mark->displaced_mark_helper() ;
//将Mark Word 搬到ObjectMonitor的_header字段里
m->set_header(dmw) ;
/