偏向锁小结(执行流程,批量重偏向和批量撤销)

1. 偏向锁执行流程

参考&图源:偏向锁_做一个坏人的博客-CSDN博客 

java - 偏向锁的【批量重偏向与批量撤销】机制

当JVM启用了偏向锁模式(JDK6以上默认开启),新创建对象的Mark Word中的Thread Id为0,说明此时处于可偏向但未偏向任何线程,也叫做匿名偏向状态(anonymously biased)。

在JVM启动时,偏向锁是延时初始化的,默认是4000ms,在这4秒创建的对象则为无锁状态 。而对于无锁状态的锁对象,如果有竞争,会直接进入到轻量级锁(因为JVM启动时会有大量的同步操作,所以跳过偏向锁,防止产生大量的偏向锁撤销,使性能降低)。 XX:BiasedLockingStartupDelay=4000

所以一般写例子会先Thread.sleep(5000),这样确保在此之后创建的锁对象是匿名偏向锁状态。

访问流程:

  1. 线程1首次访问同步块,先检测对象头的锁标志位是否为 01,代表对象锁处于无锁或偏向锁状态。

  2. 检查偏向锁标志位是否为1,若不是,说明未开启偏向锁或者已经升级为轻量级,故进入轻量级锁逻辑。

  3. 判断为偏向锁后,检查对象头的 Mark Word 的 Thread ID 是否为本线程 ID。

    • 是,则表明当前线程已经获得对象锁,直接进入同步块,并且往当前线程的栈中添加一条Displaced Mark Word为空的 Lock Record(锁记录),用来统计重入的次数(如图为当对象所处于偏向锁时,当前线程重入3次,线程栈帧中Lock Record记录)。

      偏向锁重入

      退出同步块释放偏向锁时,则依次删除对应Lock Record,但是不会修改对象头中的Thread Id;


    注:偏向锁撤销是指在获取偏向锁的过程中因不满足条件导致要将锁对象改为非偏向锁状态, 而偏向锁释放是指退出同步块时的过程。

  4. 若对象头的线程 ID不是当前线程ID,则会进行 CAS 操作,企图将 Mark Word 的 线程ID 替换成当前线程ID。如果当前对象锁状态处于匿名偏向锁状态(可偏向未锁定),则会替换成功(将Mark Word中的Thread id由 匿名的0 改成当前线程ID,在当前线程栈中找到内存地址最高的可用Lock Record,将线程ID存入),获取到锁,执行同步代码块;

  5. 如果对象锁已经被其他线程占用,则会替换失败,开始进行偏向锁撤销,这也是偏向锁的特点,一旦出现线程竞争,就会撤销偏向锁;

  6. 偏向锁撤退需要等待全局安全点,暂停持有偏向锁的线程,检查持有偏向锁的线程状态。如果线程存在并且还在同步代码块中,则升级为轻量级锁。

  7. 如果持有偏向锁的线程未存活,或者持有偏向锁的线程未在执行同步代码块中的代码,则进行校验是否允许重偏向,如果还未允许重偏向,则撤销偏向锁,将Mark Word设置为无锁状态(未锁定不可偏向状态),然后升级为轻量级锁,进行CAS竞争锁;

  8. 如果允许重偏向,设置为匿名偏向锁状态,CAS将偏向锁重新指向线程A(在对象头和线程栈帧的锁记录中存储当前线程ID);

  9. 唤醒暂停的线程,从安全点继续执行代码。

2. 批量重偏向和批量撤销

参考:批量重偏向和批量撤销理解-CSDN博客

java - 偏向锁的【批量重偏向与批量撤销】机制

注意:以下阈值针对的是同一个类的不同实例对象加锁的情况。(如Student类创建了19个实例对象作为锁,都执行了锁撤销,那么Student的第20个对象锁就会执行重偏向)

批量重偏向

首先,一开始并不会开启重偏向(意味着一有竞争就会撤销偏向锁升级为轻量级锁),当累计撤销次数达到阈值次数(20次)的时候,才会允许重偏向(设为匿名偏向状态,线程能够获取到偏向锁),重置对象头的 Thread ID。

所以前19次的锁撤销都会把锁升级成轻量级锁(无法逆转),之后的同类偏向锁对象才会执行重偏向

批量撤销

批量重偏向的重偏向只有一次机会,启动重偏向后累计执行撤销的次数达到阈值(40次,也就是重偏向后再20次)后,批量撤销,当前类的所有对象锁均升级轻量级锁,新创建的该类对象头也是无锁不可偏向状态。

epoch

epoch机制使得批量重偏向的重偏向只能执行一次

  • 每次撤销数量刚到20的时候,锁的类epoch都会+1,并且更新加锁状态中的此类锁对象(批量行为),那么那些不加锁的锁对象epoch就和类的epoch不一样了,那就可以知道哪些偏向锁是空闲的了。

  • 进行偏向时,直接就可以更改线程 ID(因为已知当前锁指向的线程ID未运行),并且将对象epoch设置成类epoch,就标志着这个锁对象已经重偏向过一次了。

再次批量重偏向?

可以设置重置时间,超时(25s)未达到批量撤销的阈值(40次)时,会重置该类的撤销次数计数器,因此批量重偏向可以进行多次。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
无状态锁、偏向锁、轻量级锁和量级锁都是Java中的锁机制,它们的实现方式和性能表现不同。 无状态锁:也称为自旋锁,当线程尝试获取锁时,如果锁已经被其他线程占用,该线程会一直自旋等待锁的释放,直到获取到锁为止。这种锁适用于锁的持有时间非常短的情况,因为长时间的自旋会浪费CPU资源。 偏向锁偏向锁是一种针对加锁操作的优化手段,它的目标是减少无竞争情况下的锁操作的性能消耗。当一个线程访问一个偏向锁时,它会将锁对象头中的标识位设置为偏向,并将线程ID记录在对象头中。之后,该线程再次请求锁时,无需再次竞争,直接获取锁即可。这种锁适用于只有一个线程访问对象的情况。 轻量级锁:轻量级锁是一种针对多线程竞争情况下的优化手段,它的目标是减少线程阻塞的时间,提高程序的并发性能。当一个线程访问一个轻量级锁时,它会将锁对象头中的标识位设置为轻量级锁,并将锁对象的指针保存在线程的栈帧中。之后,其他线程再次请求锁时,会通过自旋的方式尝试获取锁,而不是阻塞等待。如果自旋失败,锁就会升级为量级锁。这种锁适用于锁的竞争不是很激烈的情况。 量级锁:量级锁是一种针对多线程竞争情况下的优化手段,它的目标是保证线程的正确性和程序的稳定性。当一个线程访问一个量级锁时,它会进入阻塞状态,直到锁被释放。这种锁适用于锁的竞争非常激烈的情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值