小探锁的升级过程

一. 锁的状态

1.1 前言介绍

锁的状态总共有四种,级别由低到高依次为:无锁、偏向锁、轻量级锁、重量级锁

在 JDK 1.6之前,synchronized 还是一个重量级锁,是一个效率比较低下的锁,但是在JDK 1.6后,Jvm为了提高锁的获取与释放效率对(synchronized )进行了优化,引入了 偏向锁 轻量级锁 ,从此以后锁的状态就有了四种(无锁、偏向锁、轻量级锁、重量级锁)。

并且四种状态会随着竞争的情况逐渐升级,而且是不可逆的过程,即不可降级,也就是说只能进行锁升级(从低级别到高级别),不能锁降级(高级别到低级别),意味着偏向锁升级成轻量级锁后不能降级成偏向锁。

这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

1.2 四种状态

synchronized 最初的实现方式是 “阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态切换需要耗费处理器时间,如果同步代码块中内容过于简单,这种切换的时间可能比用户代码执行的时间还长”。

这种方式就是 synchronized实现同步最初的方式,这也是当初开发者诟病的地方,这也是在JDK6以前 synchronized效率低下的原因。

所以目前锁状态一种有四种,从级别由低到高依次是:无锁、偏向锁,轻量级锁,重量级锁,锁状态只能升级,不能降级。

二.锁的特点及对比

2.1 锁特点

锁状态

存储内容

标志位

无锁

对象的hashCode、对象分代年龄、是否是偏向锁(0)

01

偏向锁

偏向线程ID、偏向时间戳、对象分代年龄、是否是偏向锁(1)

01

轻量级锁

指向栈中锁记录的指针

00

重量级锁

指向互斥量的指针

11

为什么无锁/偏向锁的标志位是01,而轻量级锁的标志位是00?

即按理说,无锁是锁状态的初始情况,为什么标志位不是从00开始?查询到的一个解释,是因为 轻量级锁除了锁标志位外,另外62位都是一个指针地址。

如果将轻量级锁标志位设置为00, 那么在判断标志位为00后, m无需再额外做一次markWord>>2的操作,而是直接将markWord拿来当作地址使用即可!

2.2 锁对比

优点

缺点

适用场景

偏向锁

加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级的差距

如果线程间存在锁竞争,会带来额外的锁撤销的消耗

适用于只有一个线程访问同步块场景

轻量级锁

竞争的线程不会阻塞,提高了程序的响应速度

如果始终得不到索竞争的线程,使用自旋会消耗CPU

追求响应速度,同步块执行速度非常快

重量级锁

线程竞争不使用自旋,不会消耗CPU

线程阻塞,响应时间缓慢

追求吞吐量,同步块执行速度较慢

三.锁的升级过程

3.1 四种锁介绍

无锁:无锁是指没有对资源的锁定,所有线程都能访问并修改同一资源,但同时只有一个线程修改成功。

偏向锁初次执行到synchronized 代码块的时候,锁对象变成偏向锁(通过CAS修改对象头中的标志位),字面意思是“偏向于第一个获得它的锁”,执行完同步代码块,线程并不会自动释放偏向锁,当同一个线程第二次访问的时候,就会判断对象头中的线程是否为自己(获得锁之后,会在Mark Word 里存储当前线程的ID),不需要重新加锁,没有额外开销,性能高。

那什么时候释放锁呢? 当有其他线程尝试竞争偏向锁的时候,持有偏向锁的线程才会释放锁,注意线程是不会主动释放锁的。关于偏向锁的撤销,需要等到全局安全点,即在某个时间点上没有字节码正在执行时,它会暂停拥有偏向锁的线程,然后判断锁对象是否处于锁定状态。如果不处于活动状态则撤销,恢复到无锁或者轻量级锁状态。

轻量级锁:轻量级锁是指锁是偏向级锁的时候,却被另外的线程锁访问,此时偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能。

获取的主要两种方式:

- 当关闭偏向锁时

- 由于多个线程竞争偏向锁导致偏向锁升级为轻量级锁

重量级锁:当线程锁竞争情况严重,某个达到最大自旋次数的线程(默认为10次),会将轻量级锁升级为重量级锁(通过CAS修改所标志位,但不修改持有ID)。当后序的线程尝试获取锁时,就将自己挂起,等待被唤醒。重量级锁将控制权交给了操作系统,有操作系统来负责线程间的调度和状态变换,会出现频繁的对线程运行状态的切换,线程的挂起和唤醒,从而消耗大量的系统资源。

3.2 升级过程

检查Mark Word 里面的线程ID,如果是1,表示当前线程处于偏向锁

如果不是,将使用CAS将当前线程的ID替换Mark Word,如果成功,则表示当前线程获取偏向锁,置偏向锁的标志位为1

如果失败,则说明发生竞争,撤销偏向锁,进而升级为轻量级锁。

当前线程使用CAS将对象头的Mark Word替换为锁记录指针,如果成功,则当前线程获得锁

如果失败,则通过自旋来尝试继续获得锁。

如果自旋成功,而仍然处于轻量级锁。

自旋失败,锁升级为重量级锁。

以上图片 转载百度图片

总结

在有线程访问同步块的时候,通过检查对象头中 Mark Word 中存储的线程ID, 判断是否是偏向锁(更加倾向于一个线程对同步块的作用)。在出现锁竞争的时候,偏向锁升级为轻量级锁,在此状态下线程依然通过CAS 自旋的方式去获取锁,当自旋超过10次过后,锁升级为重量级锁。

关键理解对象的内存布局,和锁升级改变方式和对内存布局的影响

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值