无锁自旋锁偏向锁轻量级锁和重量级锁

Posted pinxiong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了无锁自旋锁偏向锁轻量级锁和重量级锁相关的知识,希望对你有一定的参考价值。

无锁

无锁是指线程通过无限循环来执行更新操作,如果执行成功就退出循环,如果执行失败(有其他线程更新了值),则继续执行,直到成功为止。CAS操作就属于无锁。如果从性能的角度来看,无锁状态的性能是非常高的。

自旋锁

自旋锁是一种通过让线程不释放当前的CPU执行一个忙循环,来尝试获取锁的方式。自旋锁的前提假设是锁被其它线程占用的时间很短。如果其它线程占用锁的时间很长,那么自旋的线程只会白白消耗处理器资源,而不会做任何有用的工作,反而带来性能上的浪费。自旋次数的默认值是10次,用户可以通过使用参数-XX:PreBlockSpin来更改。

HotSpot虚拟机对象头Mark Word

存储内容 标志位 状态
对象哈希码、对象分代年龄 01 未锁定
指向锁记录的指针 00 轻量级锁定
偏向线程ID、偏向时间戳、对象分代年龄 01 可偏向
指向重量级锁的指针 10 膨胀(重量级锁定)
空,不需要记录信息 11 GC标记

偏向锁

当一个线程获取了锁,如果在接下来的执行过程中,该锁没有被其它的线程获取,则持有偏向锁的线程将永远不需要再进行同步。当有另外一个线程区尝试获取这个锁的时候,偏向模式就宣告结束。偏向锁的前提假设是当一个线程获取锁,后面还有大概率该线程还会需要继续持有这把锁

虚拟机启用偏向锁的参数-XX:UseBiasedLocking。如果当前偏向锁已启动,当锁对象第一次被线程获取的时候,虚拟机将会把对象头中的标志位设为01,即偏向模式。同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中,如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁的同步块时,虚拟机都可以不用再进行同步操作了。

轻量级锁

在代码进入同步块的时候,如果此同步对象没有被锁定(锁标志位为01状态),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock?Record)的空间,用于存储锁对象目前的Mark?Word的拷贝。

然后,虚拟机将使用CAS操作尝试将对象的Mark?Word更新为指向Lock?Record的指针。如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark?Word的锁标志位(Mark?Word的最后2bit)将转变为00,即表示此对象处于轻量级锁定状态。轻量级锁的前提假设是对于绝大部分的锁,在整个同步周期内都是不存在竞争的,通过CAS操作来避免时候互斥锁的开销

重量级锁

当有两个及以上的线程争用同一个锁,那么轻量级锁就不再有效,要膨胀为重量级锁。锁标志的状态值变为10,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。

在讨论锁之间的转换状态时,首先需要理解以下几个问题:

  • 偏向锁只有设置了-XX:UseBiasedLocking参数才会存在
  • 假设启用了偏向锁,对象头的锁标志位是01(和未锁定状态一样),但是存储的内容是偏向线程ID、偏向时间戳
  • 当线程获取偏向锁是通过CAS操作将对象头中存储的偏向线程ID更新为当前线程的ID
  • 对象是否被锁定是指对象头是否指向线程的锁记录(Lock Record)
  • 只有是轻量级锁或者重量级锁时对象才会被锁定

锁之间状态转换

结合自己的理解绘制了一个锁之间状态转化的关系图:

技术图片

以上是关于无锁自旋锁偏向锁轻量级锁和重量级锁的主要内容,如果未能解决你的问题,请参考以下文章

(2021-04-01)常见面试题之Java和“锁”不能说的秘密

偏向锁轻量级锁和自旋锁

自旋锁阻塞锁可重入锁悲观锁乐观锁读写锁偏向所轻量级锁重量级锁锁膨胀对象锁和类锁

自旋锁,偏向锁,轻量级锁,重量级锁

图解Java中那18 把锁

javas的四种状态 无锁状态 偏向锁状态 轻量级锁状态 重量级锁状态