synchronized中的偏向锁、轻量级锁、重量级锁

对于synchronized这个关键字,可能之前大家有听过,他是一个重量级锁,开销很大,建议大家少用点。但到了jdk1.6之后,该关键字被进行了很多的优化,,建议大家多使用。

在现在的版本中,锁的状态总共有四种:

  • 无锁状态
  • 偏向锁
  • 轻量级锁
  • 重量级锁

锁的“重量”从左到右,依次递增

锁实际上是加在对象上的,那么被加了锁的对象我们称之为锁对象,在java中,任何一个对象都能成为锁对象。
为了让大家更好着理解虚拟机是如何知道这个对象就是一个锁对象的,我们下面简单介绍一下java中一个对象的结构

java对象在内存中的存储结构主要有一下三个部分:

  • 对象头
  • 实例数据
  • 填充数据

对象头的信息

长度内容说明
32/64bitMark WordhashCode,GC分代年龄,锁信息
32/64bitClass Metadata Address指向对象类型数据的指针
32/64bitArray Length数组的长度(当对象为数组时)

对象中关于锁的信息是存在Markword里的。
在这里插入图片描述

synchronized锁升级过程

在这里插入图片描述

偏向锁

一个线程操资源,此时没有其他线程会竞争,这是偏向锁,大多数情况下,锁不存在多线程竞争,而是总是由同一线程多次获得时,为了使线程获得锁的代价更低而引入了偏向锁

  • 测试对象头Mark Word(默认存储对象的HashCode,分代年龄,锁标记位)里是否存储着指向当前线程的偏向锁。
  • 若测试失败,则测试Mark Word中偏向锁标识是否设置成1(表示当前为偏向锁)
  • 没有设置则使用CAS竞争,否则尝试使用CAS将对象头的偏向锁指向当前线程

轻量级锁

轻量级锁本质是借助于CAS操作,对于竞争不激烈的场景下,可以减少重量级锁的使用
线程需要访问同步代码体时,会判断当前状态是否是无锁状态
如果无锁,将会尝试通过CAS操作,复制一份Mark Word 并且将Mark Word中的字段指向该线程栈中锁记录的指针

  • 成功说明没有竞争,那么执行同步代码体;
  • 失败说明存在竞争,那么锁会升级为重量级锁,Mark Word字段修改为指向重量级锁指针,并且请求锁的线程会被阻塞

当持有线程执行结束后,会尝试借助于CAS操作恢复Mark Down
如果有竞争会升级为重量级锁,Mark Word字段会被修改,CAS操作会失败,所以:

  • 如果此次CAS成功,锁释放完成;
  • 如果失败,将会释放锁并且唤醒被阻塞的线程

轻量级锁也被称为非阻塞同步、乐观锁,因为这个过程并没有把线程阻塞挂起,而是让线程空循环等待,串行执行。

自旋锁

所谓自旋,就是指当有另外一个线程来竞争锁时,这个线程会在原地循环等待,而不是把该线程给阻塞,直到那个获得锁的线程释放锁之后,这个线程就可以马上获得锁的。
注意,锁在原地循环的时候,是会消耗cpu的,就相当于在执行一个啥也没有的for循环。
所以,轻量级锁适用于那些同步代码块执行的很快的场景,这样,线程原地等待很短很短的时间就能够获得锁了。
经验表明,大部分同步代码块执行的时间都是很短很短的,也正是基于这个原因,才有了轻量级锁这么个东西。

重量级锁

轻量级锁膨胀之后,就升级为重量级锁了。重量级锁是依赖对象内部的monitor锁来实现的,而monitor又依赖操作系统的MutexLock(互斥锁)来实现的,所以重量级锁也被成为互斥锁。

重量级锁开销大

当系统检查到锁是重量级锁之后,会把等待想要获得锁的线程进行阻塞,被阻塞的线程不会消耗cup。但是阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态转换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

原飞木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值