《阿布的点点滴滴》:偏向锁、轻量锁与重量锁,你真的了解吗?

偏向锁与轻量锁的历史由来

 在jdk1.6之前,锁是由c++提供的objectMonitor来维护。objectMonitor 帮我们封装了阻塞队列、同步队列,加锁,释放锁...的复杂流程,其底层调用操作系统的函数来实现线程同步,以及线程切换等操作。在一些并发不高,或者甚至没有并发的场景下,这些操作很浪费系统资源。所以,jvm对此进行了优化,偏向锁、轻量锁也随之诞生!

 偏向锁

jvm是默认开启偏向锁,但有5s的延迟。

-XX:+UseBiasedLocking:开启偏向锁,-XX:-UseBiasedLocking:关闭偏向锁,-XX:BiasedLockingStartupDelay=0:设置偏向锁延迟为0,即关闭延迟。

偏向锁是基于Object的Object Header中的MarkWord来实现的,64位操作系统中,Object Header 占用128bit,Markwork占64bit。

图一

图中前8个字节即为MarkWord。可看到有个101,其余都是0,这表示此对象为可偏向但未偏向状态,可认为是Markwork的初始化的某一种情况。为什么是某一种情况呢?因为偏向锁是有延迟的,若过了延迟的时间,对象才实例化,就是这种状态;若再延迟时间内实例化的对象,其状态是一种不可偏向的无锁状态,如下图:

图二

其中的001,即为不可偏向的无锁状态,此状态是不可偏向的,所以这里我们研究的是第一种情况,即101。

当线程进入同步块时,MarkWord中会记录线程ID,表示对象锁偏向了此线程ID的线程:

图三

图三与图一比,MarkWord中发生了改变,记录了偏向的线程ID,当此线程ID的线程再进入同步块时,只要比对线程ID相同,即可获得对象锁。这也是偏向锁相对于重量锁的优化,性能会提升很多。但若是别的线程ID来访问同步块,则会先撤销偏向锁为无锁,再膨胀为轻量锁!

特地说明一下,当调用了一个对象的hashcode方法,MarkWord将不再记录线程ID,而记录对象的hashcode值,这会导致偏向锁不可用!

轻量锁

 当一个线程来访问同步块时,发现对象头的MarkWord已偏向了一个不是自己的线程ID,则会先撤销偏向锁为无锁,再cas尝试将MarkWord的前62位(若调用过hashcode方法,其中包含对象的hashcode值),替换成本线程栈针的lock_record的地址,替换成功,则获取到锁,此过程,将锁膨胀为轻量锁!来看下轻量锁的MarkWord:

图五

 图中锁的状态变为00,即轻量锁。其他位表示的是持有改锁的lock_record的地址。释放锁之后,MarkWord将恢复为不可偏向的无锁状态,如下图:

图六

可发现,MarkWord中清除了之前记录的lock_record,并还原了之前的62位的信息(若调用过hashcode方法,对象的hashcode值也随之恢复)以等待其他线程来获取锁。 此时,对象锁为无锁状态!

但是,若一个线程来获取锁,对MarkWord前62位进行cas操作,若此时MarkWord中记录的是持有锁的lock_record的地址,那这个线程将获取锁失败,接下来进行自旋。若自旋次数超过了设定的阈值,还未cas成功,则会将锁膨胀为重量级锁。

重量锁

重量锁的MarkWord中,前62位保存着objectMonitor对象的地址,当升级为重量级锁时,由monitor来维护线程的阻塞、同步、加锁、释放锁等。如下图 :

图八 

上图中,锁的状态变为10,即为重量锁。当一个线程去获取对象锁,发现其状态为10,则将此线程加入到object_monitor的同步队列中,等待锁释放之后去竞争锁。此时,锁的性能最低!

总结

我们发现,锁的膨胀过程,是伴随着并发度的提升的。随着不同的业务场景来使用合适的锁,以此来提升jvm的性能!

加餐:批量重偏向

由于一个class模板,会生成好多对象。因为偏向锁->撤销偏向锁->无锁->轻量锁,这个过程要耗费资源,当超过20个对象执行了这个过程,会进行批量重偏向。过程大致为:class对象的MarkWord的epoch将改为10,并同时这个类的其他还在同步块的对象的epoch改为10,而不在同步块的对象不改还为01,线程去获取锁时,对象的epoch值与class对象的epoch值进行比对,如不一样,则可进行重偏向,即将偏向锁的线程ID变换为另一个要获取锁的线程的ID。此处,笔者就不再演示。 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值