偏向锁、轻量级锁和重量级锁 最易懂解释

重量级锁

  • 1.每个对象都有一个对象头,对象头里有一个Mark Word结构,里面存储了锁相关信息(锁类型,偏向线程id等)。
  • 2.Mark Word中有一个指针指向monitor,当线程试图获取对象锁时(进入synchronized)自动生成,处于锁定状态。
  • 3.monitor由ObjectMonitor c++类实现,里面有: _owner(哪个线程持有锁),_count(线程持有锁的次数),_WaitSet(waiting状态的线程) 等字段。
  • 4.当线程1,2依次进入synchronized代码块时,线程1获取到了monitor,会把_owner变量设置为线程1,并且_count计数器加1。线程2进入等待_WaitSet集合进行等待。
  • 5.若对象调用wait方法/线程1执行完毕,则会进行释放锁操作:把对象头里面对应的monitor的_owner线程(线程1)放入到_WaitSet集合中(线程执行完毕情况不会),_owner变量置为null,_count减1。

重量级锁加锁解锁都是靠操作系统底层的Mutex Lock来实现的,需要用户态到和心态的切换,很耗性能。
于是jvm实现优化引入了偏向锁、轻量级锁,这些都不需要操作系统介入,直接载jvm中实现了。

偏向锁
还记得我们的每个对象都有一个Mark Word结构吗,里面存储锁信息的。

  • 当线程1初始进入synchronized块,检测到对象的Mark Word里面的存储偏向线程id字段为空,则CAS操作把线程1 的id设置到Mark Word中。
  • 此时线程1就当拿到锁了去执行代码了,关操作系统没啥事。当线程1又进入synchronized块,监视对象也是同一个,jvm爸爸检测Mark Word偏向线程id还是这家伙,就直接让它继续执行。
  • 这种情况就叫做偏向锁。在没有别的线程竞争的时候,一直偏向我,可以让我一直执行下去。

轻量级锁

  • 当有一个新的线程2来竞争了,但是Mark Word里面存的是线程1的id,所以是没法直接执行的。
  • JVM爸爸此时还是不找操作系统,只是说:我把这个偏向锁升级一下,变成一个轻量级的锁吧。
  • 于是JVM爸爸 把对象的Mark Word恢复成无锁状态。并且在线程1的栈帧中分配了Lock Record空间,并且把对象的Mark Word 拷贝到各自的Lock Record空间,取名Displaced Mark Word 。
  • JVM爸爸继续搞事,把线程1的Lock Record空间地址使用CAS放到了对象的Mark Word。并且把Mark Word中锁标志位改为00, 这其实就意味着线程1已经获得了这个轻量级的锁了,可以继续进入临界区执行。
  • 线程2没有获得锁,但还是不阻塞,JVM爸爸让它一直自旋(有指定的次数)。
  • 当线程1执行退出临界区,发生解锁操作:解锁的思路是使用 CAS 操作把当前线程的栈帧中的 Displaced Mark Word 替换回锁对象中去,如果替换成功,则解锁成功。
  • 此时自旋还未超过次数的线程2竞争,进行轻量级加锁操作(同上面线程1)。
  • 很明显,如果没有竞争或者轻度的竞争,轻量级锁仅仅使用CAS操作和Lock record就避免了重量级互斥锁的开销,JVM爸爸确实厉害。
  • 当自旋次数太多,持有线程还没释放轻量级锁,JVM爸爸受不了了: 自旋次数太多了,太浪费CPU了,接下来升级为重量级锁! 重量级锁在片头就已经介绍了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值