java如何理解互斥_Java Synchronized 重量级锁原理深入剖析上(互斥篇)

本文深入剖析Java中重量级锁的工作原理,包括ObjectMonitor的运用、锁的膨胀过程、加锁和解锁流程。通过分析揭示了锁的不公平竞争现象,对比了偏向锁和轻量级锁的区别。了解这些有助于更好地理解和优化Java多线程中的锁机制。
摘要由CSDN通过智能技术生成

前言

线程并发系列文章:

上篇文章分析了偏向锁、轻量级锁的演变过程,本篇将分析重头戏:重量级锁的原理。

通过本篇文章,你将了解到:

1、ObjectMonitor 的运用

2、锁的膨胀过程

3、重量级锁的加锁流程

4、重量级锁的解锁流程

5、重量级锁小结

6、与偏向锁、轻量级锁的比对

1、ObjectMonitor 的运用

我们知道当锁处在轻量级锁的状态时,Mark Word 存放着指向Lock Record指针,Lock Record是线程私有的。

而处在重量级锁状态时说明有线程没拿到锁需要阻塞等待锁,当拥有锁的线程释放锁后唤醒它继续竞争锁。此处就引入了一个问题:其它线程如何找到被阻塞的线程?我们很容易想到:把阻塞的线程放到多线程共享的(能访问)的列表里。

而Lock Record是线程私有的,显然不能满足需求。

因此,重量级锁引入了ObjectMonitor类。

8a8d2b42ddca

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) ;

/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值