偏向锁、轻量级锁、重量级锁、锁升级、锁对比

线程上下文切不会引起系统调用,但是开销也很大;


JVM中的锁

锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态;

Java对象头Mark Word

在这里插入图片描述

这里重点关注对象头中的Mark Word。

MarkWord里默认数据是存储对象的HashCode等信息,但是在运行期间,Mark Word里存储的数据会随着锁标志位的变化而变化

在这里插入图片描述

重量级锁也就是通常说synchronized的对象锁,锁标识位为10。
其中 栈中锁记录 是指lock record;

在这里插入图片描述


锁的四个状态:无锁状态(unlocked)、偏向锁状态(biasble)、轻量级锁状态(lightweight locked)和重量级锁状态(inflated)。


1. 偏向锁

当一个线程访问同步块并成功获取锁时,会在 对象头 的mark word栈帧中的锁记录 里存储这个的线程ID


定义

大多数情况下,锁不存在多线程竞争,而且总是由同一线程多次获得;

偏向锁 是为了消除无竞争情况下的同步操作,进一步提升程序性能。

https://www.cnblogs.com/linghu-java/p/8944784.html


偏向锁的获取过程

当一个线程访问同步块并成功获取锁时,会在 对象头 的mark word栈帧中的锁记录 里存储这个的线程ID

当一个线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着当前线程的;

如果测试成功,表示该线程已经获得了锁。  

如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否是1(1表示当前锁是偏向锁);  

如果不是1,则当前锁不是偏向锁,则使用CAS竞争锁; 

如果是1,则当前锁是偏向锁,则尝试使用CAS 将对象头的偏向锁指向当前线程;

注意,一般 的锁,在加锁成功后,会在 对象头 的mark word栈帧中的锁记录 里存储这个的线程ID。但是偏向锁加锁时,只需要对 对象头做操作,不会多对栈帧锁记录做操作;


偏向锁的撤销

首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着 ;    

如果线程处于不活动状态,则将对象头设置成无锁状态; 

如果线程仍然活着,拥有偏向锁的栈会被执行,栈中的锁记录和对象头的Mark Word要么重新偏向于其他线程,要么恢复到无锁状态 
或者 标记对象不适合作为偏向锁,最后唤醒暂停的线程

开启偏向锁

偏向锁在Java 6和Java 7里是默认启用的,但是它在应用程序启动几秒钟之后才激活(延迟激活)。

可以使用JVM参数来关闭延迟:-XX:BiasedLockingStartupDelay=0。

如果应用程序里所有的锁通常情况下处于竞争状态,可以通过JVM参数关闭偏向锁:-XX:-
UseBiasedLocking=false,那么程序默认会进入轻量级锁状态。


2. 轻量级锁

如上文 所叙,jvm默认开启偏向锁,当关闭偏向锁时,那么默认进入轻量级锁状态;

https://www.cnblogs.com/deltadeblog/p/9559035.html

https://blog.csdn.net/qq_35124535/article/details/70312553

在这里插入图片描述

线程在执行同步块之前,JVM会先在 当前 线程 的 栈桢 中 创建用于存储锁记录的空间(lock record),并将对象头中的Mark Word复制到锁记录中。

然后 线程尝试使用 CAS 将对象头的Mark Word中的锁指针 指向锁记录lock record

如果成功,当前线程获得锁(并且对象Mark Word的锁标志位设置为“00”,即表示此对象处于轻量级锁定状态),如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋(循环等待)来获取锁。


轻量级锁解锁

使用CAS操作将 Displaced Mark Word(锁记录 lock record) 替换回到对象头;

如果成功,则说明没有发生竞争
失败,则表示当前锁存在竞争,锁就会膨胀成重量级锁;


3. 重量级锁

其他线程试图获取锁时,都会被阻塞,只有持有锁的线程释放锁之后才会唤醒这些线程,进行竞争。


锁对比

在这里插入图片描述

总结:  
偏向锁 只适用于只有一个线程访问同步代码块的情形,一旦遇到竞争,就会升级为轻量级锁;  
可以理解为偏向锁不存在竞争;    

轻量级锁 存在竞争,但是竞争失败的线程 不会被阻塞,而是通过 自旋 等待再次获取锁;但是自旋消耗cpu; 

而 重量级锁,竞争失败的线程汇会被阻塞;

锁升级

无锁 → 偏向锁

无锁 升级 为 偏向锁 就是 偏向锁 第一次获取锁的过程;

访问同步代码时,对象头没有存储线程id,直接使用cas将mark word 线程id 改为当前线程id;


偏向锁 -> 轻量级锁

当有第二个线程竞争锁时,则升级为轻量级锁


轻量级锁 → 重量级锁

轻量级锁 发生竞争时需要通过自旋 等待获取锁,但是如果自旋的时间太长也不行,因此自旋的次数是有限制的,如果自旋次数到了线程还没有释放锁,或者线程1还在执行,线程2还在自旋等待,这时又有一个线程3过来竞争这个锁对象,那么这个时候轻量级锁就会膨胀为重量级锁。

重量级锁把除了拥有锁的线程都阻塞,防止CPU空转。

总结:

  1. 当有另一个线程与该线程同时竞争时,自旋到一定次数还未获取到锁,锁会升级为重量级锁。
  2. 有多个线程(大于2)竞争锁;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值