偏向锁、轻量级锁、重量级锁升级过程中的疑问解答

1 背景

在https://blog.csdn.net/qq_33996921/article/details/106629770这篇文章《Synchronized关键字和锁升级(偏向锁、轻量级锁、重量级锁)》中讲解了Synchronized使用原理,及锁的升级,在翻阅资料以及看别的博客中一些读者遇到的疑问,本文主要针对在锁升级中的一些问题进行解释。

2 锁升级的流程图

先从《并发编程的艺术》这本书中摘抄了两个锁升级的图:

1、偏向锁的获得和撤销流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jcraaem2-1592059245925)(S:\01_学习文档\B_csdn\07_concurrent\图片\偏向锁.png)]

2、轻量级锁及膨胀流程

在这里插入图片描述

以上锁的过程不做详细的赘述,在上一篇提到的文章中已做了详细描述,下面针对一些疑问做相应的解答。

3、锁对象头

对象头的信息:

在这里插入图片描述

对象 MarkWord 状态以不同方式转换的过程如下:

在这里插入图片描述

翻译一下:

在这里插入图片描述

4 锁升级中的问题

1、问题当线程A 获取了偏向锁,此线程B进来了,走到了同步代码块前,此时是的锁是如何变化的,看到文章中:线程B CAS 失败会发起偏向锁撤销。 此处是线程A 升级为轻量级锁,还是重新争抢? 如果是重新争抢,失败的线程会怎样?

线程A修改MarkWord中的信息,修改锁的标识为00,升级为轻量级锁;A已经持有偏向锁,从偏向锁升级为轻量级锁,是不需要再去重新争抢的,线程B会自旋尝试获取轻量级锁;

2、线程A、线程B同时到来,是不是直接就走轻量级锁,而跳过偏向锁,此是争抢失败的线程,发生自旋操作,如果自旋失败,我看文章是升级到重量级锁,那么是线程A、B同时升级到重量级锁的争抢吗?

走不走偏向锁是跟开不开启偏向锁有关的,可以用idea进行测试,加入参数-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0AB线程中,A线程先持有锁,那么B自旋失败后就会把锁膨胀成重量级锁,这时候A就是持有了重量级锁,B就是处于阻塞队列了(被放在_entryList里),等A释放后,B才有机会获取到锁;

3、 jvm 是不是会自行根据当前并发,直接选择是采用偏向锁、轻量级、重量级,还是必须是从偏向锁过渡到重量级锁?

偏向锁默认是不开启的,如果要开启可以用上面第(2)说的方法,不开启就默认先获取到轻量级锁,然后再升级到重量级锁,这其实是对锁的一种优化。因为加锁、释放锁的成本,要比自旋等待获取锁的成本高很多。

4、ThreadA在获得轻量级锁后,其RecordLock里存储着锁对象头的Mark word信息,将锁对象的Mark Word更新为指向Lock Record的指针,在ThreadB自旋失败后,进行锁膨胀,修改为重量级锁,这里有三个问题:
1)ThreadB修改为重量级锁,是修改的 Lock Record中存储的Mark word的信息么?

2)此时锁对象的Mark Word指向的Lock Record的指针是 ThreadB的么?

3)在ThreadB将锁膨胀为重量级锁之后,正在执行中的ThreadA怎么处理?

1)线程B膨胀为重量级锁,是基于Monitor了,不是再修改markword;

2)所以第二个问题不存在;

3)锁膨胀为重量级锁之后,ThreadA继续运行,直到运行结束。

5、线程B多次自旋失败后,将锁升级为重量级锁之后,线程A就直接拥有了这把重量级锁,然后Monitor对象中_owner存储的是线程A的地址指针,而线程B则加入到__EntryList队列中进行阻塞,等待被唤醒,那此时,锁对象里的对象头里的Mark Word信息是不会改变的,仍然存储的是A的指针,是等到线程A释放锁的时候再去将Mark Word里的锁标识进行修改么(修改为重量级锁10)

Mark word存储的不再是轻量级锁中指向Lock Record的指针,升级为重量级锁之后存储的就是Monitor了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章