Java 描述的 synchronized 加“锁“ 到底是啥?

导读:本篇文章讲解 Java 描述的 synchronized 加“锁“ 到底是啥?,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

多线程同步的锁(Monitor) 和 Java对象头(Object Header)密切相关

Java 对象头

在JVM中,对象在内存中的布局分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
yrZFz9.png
HotSpot 虚拟机的对象头(Object Header)包括两部分信息:

  • 第一部分,是用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为Mark Word

  • 另一部分,是类型指针,即是对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说查找对象的元数据信息并不一定要经过对象本身。

以 32 位虚拟机为例

普通对象

如果对象是普通对象类型,则用 2 byte 存储对象头。

yre54S.png
对象的类型,如Thread 类型,Integer类型等,都是由KlassWord来表示的,它是一个指针,指向了对象所从属的Class对象,即找到类。

其中 Mark Word 结构为:
yrevNT.png
其中,

  • age表示垃圾回收时的分代年龄,具体可见分代收集算法。年龄超过一定岁数就从幸存区调入到老年代。

  • biased_lock01 表示偏向锁和加锁状态。其它表示在不同状态下每一位所代表的含义。

  • Normal状态表示对象的正常状态。对它加各种锁的时候,其值通常会改变。

数组对象

如果对象是数组类型,则虚拟机用 3 byte 存储对象头

yrm1bt.png

注意:在一个Integer对象里,要保存8个字节的对象头,还有4个字节的int类型的数据。总共12字节,而基本数据类型int则只需4个字节。在内存敏感的情况下,建议用基本类型。

什么是 Monitor(锁)?

以上可以看到, 任何一个 Java 对象都可以通过关联一个 Monitor 对象,成为锁。

例如:如果使用 synchronized 给对象Object.class 上锁之后,该对象的对象头的Mark Word 中就被设置指向 Monitor 对象的指针。

yrnOYT.png
所以,可以简单的理解为, Monitor 就是操作系统创建的一个对象。

Monitor 的工作原理

当我们线程2执行如下代码时:

synchronized(Object.class){
    //处理相关业务
}
  1. Thread2 线程执行上述代码时,当前对象Object.class会被上一把锁,该对象的对象头的MarkWord字段指向了操作系统创建的Monitor对象

注:MarkWord 在没有加任何锁的时候,即Normal状态,标记位为01。一旦要获取锁,就会尝试找一个Monitor与之关联,然后把最后两位也即标记位从01改为10,并且把前面的所有位改成指向Monitor对象的指针,占用30位。此时由于只有Thread2线程,所以成功获取了锁。理所应当的成为了Monitor对象的Owner。

Owner表示Monitor锁的持有者,而且同一个时刻只能有一个Owner

  1. Monitor对象只能有一个Owner,此时如果有其它线程如 Thread-3Thread-4 等线程要获取这把锁就要进入Monitor对象的堵塞队列EntryList中等待Thread2释放锁。

注:EntryList 可以理解位 阻塞队列或同步队列。一直等待到其它线程释放Owner的所有权。

  1. 等待锁资源被释放后,Thread-3或Thread-4 会互相竞争锁资源,并不能保证谁获取到锁,最终还是有CPU来决定。
  2. Monitor对象的WaitSet存放的是,获取到锁的线程,但是由于其它一些原因导致线程进入WAITING状态,又释放了锁资源。

注:EntryList 中的线程都处于 BLOCKED, WaitSet 中的线程都处于 WAITING

原理如图:
yrMRFx.png

  1. 刚开始 Monitor 中 Owner 为 null;
  2. 当 Thread-2 执行 synchronized(Object.class), 就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor中只能有一个 Owner;
  3. 在 Thread-2 上锁的过程中,如果 Thread-3,Thread-4,Thread-5 也来执行 synchronized(Object.class),就会进入 EntryList, 同时变为 BLOCKED 状态;
  4. Thread-2 执行完同步代码块的内容,然后唤醒 EntryList 中等待的线程来竞争锁,竞争的时是非公平的;
  5. 图中 WaitSet 中的 Thread-0,Thread-1 是之前获得过锁,但条件不满足进入 WAITING 状态的线程。

注:synchronized 必须是进入同一个对象的 Monitor 才有上述的效果,不加 synchronized 的对象不会关联监视器。

附录

Java多线程学习之wait、notify/notifyAll 详解

Java并发编程-synchronized底层原理

synchronized详解

Java Synchronized 重量级锁原理深入剖析下(同步篇)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/69727.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!