【Java并发编程】synchronized(六):偏向锁与 HashCode 能共存吗?

原文链接:https://blog.csdn.net/saintyyu/article/details/108295657

我们知道,Java 对象头的结构如下:

内容说明备注
Mark Word存储对象的Mark Word信息-
Class Metadata Address存储指向对象存储类型的指针-
Array Length数组的长度只有数组对象有该属性

其中,在32位下,Mark Word 的存储结构如下:

在这里插入图片描述

在64位下,Mark Word的存储结构如下:

在这里插入图片描述

由此可知,在无锁状态下,Mark Word 中可以存储对象的 identity hash code 值。当对象的 hashCode() 方法(非用户自定义)第一次被调用时,JVM 会生成对应的 identity hash code 值,并将该值存储到 Mark Word 中。后续如果该对象的hashCode() 方法再次被调用则不会再通过 JVM 进行计算得到,而是直接从 Mark Word 中获取。只有这样才能保证多次获取到的 identity hash code 的值是相同的(以jdk8为例,JVM 默认的计算 identity hash code 的方式得到的是一个随机数,因而我们必须要保证一个对象的 identity hash code 只能被底层 JVM 计算一次)。

我们还知道,对于轻量级锁,获取锁的线程栈帧中有锁记录(Lock Record)空间,用于存储 Mark Word 的拷贝,官方称之为 Displaced Mark Word,该拷贝中可以包含 identity hash code,所以轻量级锁可以和 identity hash code 共存;对于重量级锁,ObjectMonitor 类里有字段可以记录非加锁状态下的 mark word,其中也可以存储 identity hash code 的值,所以重量级锁也可以和 identity hash code 共存。

对于偏向锁,在线程获取偏向锁时,会用 Thread ID 和 epoch 值覆盖 identity hash code 所在的位置。如果一个对象的hashCode() 方法已经被调用过一次之后,这个对象还能被设置偏向锁么?答案是不能。因为如果可以的化,那 Mark Word中 的 identity hash code 必然会被偏向线程Id给覆盖,这就会造成同一个对象前后两次调用 hashCode() 方法得到的结果不一致。

HotSpot VM 的锁实现机制是:

  • 当一个对象已经调用默认 hashCode() 或者 System.identityHashCode(),即计算过 identity hash code 后,它就无法进入偏向锁状态。这意味着,如果要在不发生争用的对象上进行同步,则最好覆盖默认hashCode()实现,否则JVM不会优化。
  • 当一个对象当前正处于偏向锁状态,并且需要计算其 identity hash code 的话,则它的偏向锁会被撤销,并且锁会膨胀为轻量级锁或者重量锁;
  • 轻量级锁的实现中,会通过线程栈帧的锁记录存储 Displaced Mark Word;
  • 重量锁的实现中,ObjectMonitor 类里有字段可以记录非加锁状态下的 mark word,其中可以存储 identity hash code 的值。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值