多线程同步的锁(Monitor) 和 Java对象头(Object Header)密切相关
Java 对象头
在JVM中,对象在内存中的布局分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
HotSpot 虚拟机的对象头(Object Header)包括两部分信息:
-
第一部分,是用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为
Mark Word
。 -
另一部分,是类型指针,即是对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说查找对象的元数据信息并不一定要经过对象本身。
以 32 位虚拟机为例
普通对象
如果对象是普通对象类型,则用 2 byte 存储对象头。
对象的类型,如Thread
类型,Integer
类型等,都是由KlassWord
来表示的,它是一个指针,指向了对象所从属的Class对象,即找到类。
其中 Mark Word
结构为:
其中,
-
age
表示垃圾回收时的分代年龄,具体可见分代收集算法。年龄超过一定岁数就从幸存区调入到老年代。 -
biased_lock
和01
表示偏向锁和加锁状态。其它表示在不同状态下每一位所代表的含义。 -
Normal
状态表示对象的正常状态。对它加各种锁的时候,其值通常会改变。
数组对象
如果对象是数组类型,则虚拟机用 3 byte 存储对象头
注意:在一个
Integer
对象里,要保存8个字节的对象头,还有4个字节的int
类型的数据。总共12字节,而基本数据类型int则只需4个字节。在内存敏感的情况下,建议用基本类型。
什么是 Monitor(锁)?
以上可以看到, 任何一个 Java 对象都可以通过关联一个 Monitor 对象,成为锁。
例如:如果使用
synchronized
给对象Object.class
上锁之后,该对象的对象头的Mark Word
中就被设置指向Monitor
对象的指针。
所以,可以简单的理解为, Monitor 就是操作系统创建的一个对象。
Monitor 的工作原理
当我们线程2执行如下代码时:
synchronized(Object.class){
//处理相关业务
}
- Thread2 线程执行上述代码时,当前对象
Object.class
会被上一把锁,该对象的对象头的MarkWord
字段指向了操作系统创建的Monitor
对象。
注:
MarkWord
在没有加任何锁的时候,即Normal状态,标记位为01。一旦要获取锁,就会尝试找一个Monitor与之关联,然后把最后两位也即标记位从01改为10,并且把前面的所有位改成指向Monitor对象的指针,占用30位。此时由于只有Thread2线程,所以成功获取了锁。理所应当的成为了Monitor对象的Owner。Owner表示Monitor锁的持有者,而且同一个时刻只能有一个
Owner
。
- Monitor对象只能有一个Owner,此时如果有其它线程如 Thread-3 或 Thread-4 等线程要获取这把锁就要进入Monitor对象的堵塞队列
EntryList
中等待Thread2释放锁。
注:
EntryList
可以理解位 阻塞队列或同步队列。一直等待到其它线程释放Owner的所有权。
- 等待锁资源被释放后,Thread-3或Thread-4 会互相竞争锁资源,并不能保证谁获取到锁,最终还是有CPU来决定。
- Monitor对象的
WaitSet
存放的是,获取到锁的线程,但是由于其它一些原因导致线程进入WAITING
状态,又释放了锁资源。
注:
EntryList
中的线程都处于BLOCKED
,WaitSet
中的线程都处于WAITING
。
原理如图:
- 刚开始
Monitor
中 Owner 为 null; - 当 Thread-2 执行
synchronized(Object.class)
, 就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor中只能有一个 Owner; - 在 Thread-2 上锁的过程中,如果 Thread-3,Thread-4,Thread-5 也来执行
synchronized(Object.class)
,就会进入EntryList
, 同时变为BLOCKED
状态; - Thread-2 执行完同步代码块的内容,然后唤醒
EntryList
中等待的线程来竞争锁,竞争的时是非公平的; - 图中
WaitSet
中的 Thread-0,Thread-1 是之前获得过锁,但条件不满足进入WAITING
状态的线程。
注:synchronized 必须是进入同一个对象的 Monitor 才有上述的效果,不加 synchronized 的对象不会关联监视器。
附录
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/69727.html