重量级锁、轻量级锁和偏向锁

一、需要了解的结构:

1.java普通对象头结构(数组不同,不赘述)

在这里插入图片描述
Mark word: 25位hashcode+4位分代年龄+3位锁标记

2.线程栈帧结构

在这里插入图片描述
Object reference: 用于指向对象地址
LockRecord: 锁记录对象,jvm层面,记录锁记录的地址和加锁状态

3.对象结构

在这里插入图片描述

二、重量级锁(Monitor)

在这里插入图片描述

1.正常情况下对象的Mark Word部分记录的是hashcode等信息;当对象被上锁以后,对象的Mark Word记录Monitor地址
2.当线程2需要调用obj时,会检查obj对应的Monitor的Owner是否已经被其他线程占用,如果没有,线程2占用obj
3.当其他的线程想要调用obj时候先看一下Monitor的Owner有没有被其他的线程占用,如果被占用,就挂到阻塞队列等待;
4.线程2使用完成后唤醒等待队列的线程,这些线程对obj进行竞争(非公平竞争,竞争方法由jdk实现)

问题: 重量级锁中的阻塞需要转入内核态中完成,花费时间多。很多时候不会有一个线程长时间占用同一个对象资源

三、轻量级锁

使用情景:一个对象有多个进程访问,但是线程访问的时间是错开的,用轻量级锁优化
在这里插入图片描述
1.Thread-0要使用obj,先在自己的栈中创建一个锁记录对象(Lock Record 各部分结构上面有提到)
2.检查obj的Markword字段,如果没有被上锁(结尾是01)就将LockRecord与MarkWord交换(这个比较交换过程叫cas,失败的情况之后说)
3.当Thread-0使用完成后在将Markword和LockRecord交换回来
cas失败情况:
[1] Thread-0自己再次申请obj(此时obj被Thread-0上锁,还没有被使用完)----锁重入
在这里插入图片描述

<1>再向Thread-0的栈中添加一个Lock Record对,并且将LockRecord字段为null
<2>以后每解锁一个为null的就去掉一个Lock Record栈帧,直到最后一个LockRecord为null的栈帧被去掉,开始进行还原过程
<3>启用cas过程,若成功正常解锁obj,如果不成功证明进行了锁膨胀或者已经升级为重量级锁,进入重量级锁的流程
[2] Thread-1也想要给obj上锁,但是obj已经别Thread-0占用(锁膨胀)
在这里插入图片描述

<1>Thread-1尝试与obj进行cas,obj被占用,cas失败
<2>为obj申请一个Monitor,obj的Markword指向Monitor地址
<3>Monitor的Owner指向Thread-0,Thread-1被阻塞,进入Monitor的阻塞队列
<4>当Thread-0使用完成,将Owner置为null,唤醒Monitor阻塞队列进行对obj的竞争

问题: <1>Thread-0占用时间不长,但是Thread-1与Thread-0冲突时马上转化为重量级锁耗费时间;<2>同一个线程一直重复给对象加锁,其他线程很少给对象加锁时会出现大量的cas操作,浪费时间

四、自旋优化

当Thread-1想要为obj上锁,但是obj已经被Thread-0占用时,Thread-1不马上进入阻塞态,而是自旋等几次,如果Thread-0释放obj就让Thread-1使用,否则进入阻塞队列(只使用多核cpu)
Java6会根据前几次的自旋结果设置自旋次数,如果前面有成功地例子那就多自旋几次,jdk7以后就不能控制自旋次数了

五、偏向锁

在这里插入图片描述

<1>Thread-0获得锁后,将obj的markword换成Thread-0的id
<2>以后每次都比较id是否相同,相同证明Thread-0已经获得锁,不用锁重入
<3>如果有新的Thread-1想获得锁,进入锁膨胀,偏向锁变为轻量级锁

Tips:
[1]轻量级锁要进行每次都要cas更新markword,偏向锁默认markword就是一个线程的id不会自己改变所以偏向锁无法自旋优化
[2]调用wait/notify时会将偏向锁和轻量级锁都升级为重量级锁
[3]当一个可以偏向的对象被调用Hashcode时会撤销该对象的偏向状态(因为hashcode占用mark word的)
31位,而线程id用54位,Markword一共64,不够用)

批量重偏向
当偏向锁撤销超过20次的时候,会执行批量重偏向
批量撤销
当偏向锁撤销超过40次后,证明此对象的竞争比较激烈,所以将它置为不可以偏向状态
锁削除
场景: 实际过程中,很有可能很多加锁是无效的(如局部变量作为锁,由于每次都是新对象新锁,所以没有意义)
虚拟机即时编译器(JIT)运行时,依据逃逸分析的数据检测到不可能存在竞争的锁,就自动将该锁消除

轻量级重量级偏向锁Java中的三种不同的机制。轻量级是一种乐观,用于在多线程环境下提高并发性能。当竞争不激烈时,轻量级使用CAS(Compare and Swap)操作来获取,避免了线程阻塞,提升了性能。然而,如果竞争激烈,轻量级会膨胀为重量级偏向锁是在没有竞争的情况下,为了进一步提高性能而引入的机制。偏向锁允许第一个获得的线程在接下来的执行中,无需再进行同步操作,从而减少了不必要的竞争。当有其他线程试图获取偏向锁时,偏向锁会升级为轻量级重量级重量级是一种传统的互斥,它使用操作系统原语(例如互斥量)来实现线程阻塞和唤醒,确保同一时间只有一个线程可以访问被定的资源。重量级适用于竞争激烈的场景,但由于涉及到内核态和用户态之间的切换,会带来较大的性能开销。 总结起来,轻量级偏向锁都是为了提高并发性能而引入的机制,轻量级适用于竞争不激烈的场景,而偏向锁适用于没有竞争的情况下。重量级适用于竞争激烈的场景,但会带来较大的性能开销。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [浅谈偏向锁轻量级重量级](https://blog.csdn.net/yaojiqic/article/details/124619021)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Java中的偏向锁轻量级重量级解析](https://blog.csdn.net/lengxiao1993/article/details/81568130)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java的升级策略 偏向锁 轻量级 重量级](https://download.csdn.net/download/weixin_38512659/12749004)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值