多线程:synchronized的底层实现原理及锁的升级

1、synchronized的使用

a)普通同步方法,锁的是当前实例对象
b)静态同步方法,锁的是当前类对象
c)同步方法块,锁的是Synchronized括号中配置的对象
当一个对象需要方法同步方法或代码块时,需要获取锁,退出或者抛出异常时,必须释放锁

2 Synchronized底层实现

JVM基于进入和退出Monitor对象来实现方法同步和代码块的同步,但是两者的实现细节不同。
代码块同步使用的是monitorenter和monitorexit指令实现的,而同步方法使用的是ACC_SYNCHRONIZED标识实现的。
获取锁与Monitor对象相关,我们知道对象实例的组成包括:对象头、实例数据、对齐填充三部分
在这里插入图片描述
对象头中包含两部分信息:用于存储对象自身的运行时数据(Mark Word),如哈希码、GC分代年龄、锁状态标志、线程持有的锁等信息;第二部分为类型指针,对象指向它的元数据的指针,虚拟机通过这个指针确定这个对象是哪个类的实例。
Synchronized对象锁,其指针指向的是一个monitor对象(C++实现)的起始地址,每个对象实例都会有一个monitor,可以与对象一同创建或者销毁,或者当线程试图获取锁时自动生成。现实monitor对象的是ObjectMonitor,其结构如下:
在这里插入图片描述
解释其中字段的意思:
_cout: 当一个线程获取对象的monitor是,该值+1,如果推出是减一
_owner: 存储当前获取monitor的线程
_WaitSet: 线程获取了锁,使用wait进行了等待,则进入到该队列中
_EntryList: 所有想获得锁的线程进入到该队列中
在这里插入图片描述
当有多个线程同时访问不同代码时,线程先进入_EntryList中,去竞争锁,当一个对象获取锁之后,_owner标识为自己,count+1。如果调用wait方法,则进入_waitSet中阻塞等待唤醒,同时count-1,owner为null

锁的升级

锁的状态一个包括四种:无状态锁、偏向锁、轻量级锁和重量级锁,这几个状态会随着竞争的情况逐渐升级。锁可以升级,不能降级。这种策略是为了提高获取锁和释放锁的效率。

偏向锁:锁不存在竞争,且总是由同一线程多次获得,为了让线程获取锁的代价更低,引入了偏向锁。当一个线程访问同步代码时,会在对象头和栈帧中的锁记录中存储锁偏向的线程ID,以后该线程进入和退出同步代码时,不需要进行CAS加锁和解锁。只是简单测试一下对象头中的Mark Word是否存储了指向该线程的偏向锁。Mark Word 中偏向锁标识为1则表示偏向锁,如果没有,则使用CAS竞争锁。
偏向锁的释放:当有其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。偏向锁的撤销,需要等待安全点(在这个时间点上,没有正在执行的字节码)。他会首先暂停拥有偏向锁的线程,检查该线程是否活着,如果线程不处于活动状态,则对象头设置成无锁状态,如果仍然活着,持有偏向锁的栈会被执行。

轻量级锁:线程在执行同步块之前,JVM会先在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头中的Mark Word 复制到锁记录中。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值