10.对象头、Mark Word、monitor、synchronized怎么关联起来?

老王:开讲啦,开讲啦,小陈快来上课,今天我们就来探讨一下synchronized底层到底是怎么加锁的?

小陈:哈哈,这个我可等了好久了。

从我接触java开始,就知道了java里面有synchronized这个关键字,是用来加锁的,但是它底层是怎么加锁的我一直没搞明白。

看了一些文章,说通过一个monitor监视器还有什么monitorenter、monitorexit这两条执行来进行加锁和释放锁的,但是具体底层一点的细节我就不知道了,一直是我单身二十几年的一个遗憾啊......

老王:哦,听你这么说,看来你还研究过啊,那你来说说怎么通过monitor和monitorenter和monitorexit来加锁和释放锁的?

小陈:好啊,我结合之前看过的一些文章,画个图来说一下我的理解:

(1)首先java里面每个对象JVM底层都会为它创建一个监视器monitor,这个是JVM层次为我们保证的。这个监视器就类似一个锁哪个线程持有这个monitor的操作权,就相当于获取到了锁

(2)其次synchronized 修饰的代码或者方法,底层会生成两条指令分别为monitorenter、monitorexit。

(3)进入synchronized的代码块之前会执行monitorenter指令,去申请monitor监视器的操作权,如果申请成功了,就相当于获取到了锁。

如果已经有别的线程申请成功monitor了,这个时候它就得等着,等别的线程执行完synchronized里面的代码之后就会执行monitorexit指令释放monitor监视器,这样其它在等待的线程就可以再次申请获取monitor监视器了。

老王:你说的大致正确,但是表面了一点,这个很多文章都讲到了。但是其实synchronized底层加锁其实涉及很多细节的东西。

小陈:嗯嗯,我看很多文章基本都是只是讲到了通过monitorenter、monitorexit指令去获取monitor的操作权,也就是获取锁,但是到底是怎么获取的?monitor又是个啥东西?为什么monitor能当做锁?这些更底层的细节我就不知道了。

老王:哈哈,我会讲得通俗一点,尽量让你能听得懂......

老王:首先既然你知道每个对象都有一个monitor监视器,那你知道每个对象是怎么和它的monitor监视器关联起来的不?

小陈:额额额,这个不懂......

老王:说起这个对象和monitor的关联关系,我首先给你讲一下java对象的结构

JAVA对象结构

老王:假如说有一个Test类如下:

public class Test {
    private int a = 8;
    private ArrayList list = new ArrayList();
}

老王:那么Test对象结构如下图所示:

一个java实例对象由三部分组成,分别是:

对象头:对象头由Mark Word 和 一个指向一个类对象的指针组成。

实例数据存放这个实例的一些属性信息,比如有的属性是基本类型,那就直接存储值;如果是对象类型存放的就是一个指向对象的内存地址

对其填充:主要是补齐作用,JVM对象的大小比如是8字节的整数倍,如果 (对象头 + 实例变量 )不是8的整数倍,则通过对齐填充来补齐。

比如 (对象头 + 实例变量) 部分的大小是20个字节不是8的整数倍,那么对齐填充这里就会补上4个字节,使得(对象头 + 实例变量 + 对其填充)= 24字节,为8的整数倍。

老王:上面就是一个对象的结构,这个小陈你听懂了吗?

小陈:有点懂,又有点不懂,能不能再画个图说一下,对象头,实例变量里面是啥东西?

老王:没问题,比如说Test test = new Test(),我就来再画个图细说一下test对象的结构:

如上图所示:

(1)对象头:由Mark Word 和一个指向类对象的指针组成

(2)实例变量:记录这个对象哪些属性,如果是基本类型,直接就记录值了;如果是对象类型,则记录一下对象在堆内存的地址,方便以后找到它来使用

(3)对其填充:这块东西就是为了对象的大小满足8的整数倍,进行补齐的,别的鸟用没有

老王:小陈,我这么说,你理解了没?

小陈:哇塞,牛逼啊,你这么说整个对象的结构一目了然......

小陈:老王啊,上面那张图,我还有一个问题,那个Mark Word到底是个啥东西?

老王:Mark Word啊,那就是我们后面讲解的重点了,我们通过synchronized进行加锁,就是通过Mark Word关联起来的。

老王:为了讲清楚Mark Word,我写了一篇Mark Word的自述,让它自己说下它是干啥用的。

JVM世界的Mark Word自述

大家好,我是来自JVM世界的Mark Wold,是一个32位的数据结构存在于对象头里面,大致结构如下:

大家都知道,计算机由0和1组成的每bit位不是0就是1,所以一个bit能表示两种不同的意思,所以啊,我具有32个bit位,能表示的意思非常多

JVM就根据Mark Word上32bit的值不同,把我设计为一个多功能的复用器,在bit标志位不同的时候表示的意思也不一样,我前面30bit位可能表示的意思不一样,但是最后2个bit表示的都是锁模式,下面我用表格说明一下我的作用:

(1)首先我的最后两位,也就是锁标志位,分别标识处于不同的锁模式;倒数第3位偏向锁标志

(2)当我的偏向锁标志是0锁标志位是01,也就是最后3位是001的时候,我表示无锁模式。作为Mark Word的我就是记录的数据就是对象的hashcode 和 GC的年龄

(3) 当我的偏向锁标志是1锁标志是01,也就是最后三位是101的时候,处于偏向锁模式,我作为Mark Word这个时候记录的数据就是是获取偏向锁的线程IDEpoch对象GC年龄

(4)当我的锁标志位是00的时候,表示处于轻量级锁模式。我会把锁记录放在加锁的线程的虚拟机栈空间中,所以这种情况下锁记录在哪个线程虚拟机栈中,就表示所在线程就获取到了锁

然后我作为Mark Word记录的数据就是就指向那个锁记录地址就好了,这个锁记录地址在哪个线程中,就表示哪个线程获取到了轻量级锁

(5)当我锁标志位是10的时候,表示处于重量级锁模式,这个时候就说明竞争激烈了,处于重量级锁模式了,由于使用重量级加锁不是我的职责范围是我的哥们monitor的职责,我这里有它的地址,你们去那里找他吧。

这个是我作为Mark Word 记录的数据就是我哥们monitor的地址,你们有加锁的需求直接根据我提供的这个地址找到monitor,找它加锁就好了。

老王:小陈,这就是这一节要讲的JAVA对象结构以及Mark Word,你听懂了吗?

小陈:懂了,懂了,牛逼啊,老王。感觉你把Mark Word的裤子都给扒了啊......

老王:混小子,有你这么说话的......

小陈:嘿嘿,老王,Mark Word里面的结构我是看懂了,但是我看好多人说底层可以通过Mark Word 可以进行锁升级,无锁、偏向锁、轻量级锁、重量级锁、重量级锁中的自旋等,这个它是怎么操作的?monitor又是个什么结构?以及在重量级锁的时候怎么通过monitor进行加锁的?这些问题我都没明白

老王:别急啊,小陈,这些东西我们下面都会来讲到的,现在我们先顺着思路来一点点讲解。

小陈:好啊,老王,那我们接下面学习什么?

老王:下一篇,我们顺着思路来讲讲《synchronized底层是怎么通过monitor进行加锁的?

小陈:好的,老王,我们下一章见面。

关注小陈,公众号上更多更全的文章

JAVA并发文章目录(公众号)

JAVA并发专题 《筑基篇》

1.什么是CPU多级缓存模型?

2.什么是JAVA内存模型?

3.线程安全之可见性、有序性、原子性是什么?

4.什么是MESI缓存一致性协议?怎么解决并发的可见性问题?

JAVA并发专题《练气篇》

5.volatile怎么保证可见性?

6.什么是内存屏障?具有什么作用?

7.volatile怎么通过内存屏障保证可见性和有序性?

8.volatile为啥不能保证原子性?

9.synchronized是个啥东西?应该怎么使用?

10.synchronized底层之monitor、对象头、Mark Word?

11.synchronized底层是怎么通过monitor进行加锁的?

12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁

13.synchronized怎么保证可见性、有序性、原子性?

JAVA并发专题《结丹篇》

14. JDK底层Unsafe类是个啥东西?

15.unsafe类的CAS是怎么保证原子性的?

16.Atomic原子类体系讲解

17.AtomicInteger、AtomicBoolean的底层原理

18.AtomicReference、AtomicStampReference底层原理

19.Atomic中的LongAdder底层原理之分段锁机制

20.Atmoic系列Strimped64分段锁底层实现源码剖析

JAVA并发专题《金丹篇》

21.AQS是个啥?为啥说它是JAVA并发工具基础框架?

22.基于AQS的互斥锁底层源码深度剖析

23.基于AQS的共享锁底层源码深度剖析

24.ReentrantLock是怎么基于AQS实现独占锁的?

25.ReentrantLock的Condition机制底层源码剖析

26.CountDownLatch 门栓底层源码和实现机制深度剖析

27.CyclicBarrier 栅栏底层源码和实现机制深度剖析

28.Semaphore 信号量底层源码和实现机深度剖析

29.ReentrantReadWriteLock 读写锁怎么表示?

30. ReentrantReadWriteLock 读写锁底层源码和机制深度剖析

JAVA并发专题《元神篇》并发数据结构篇

31.CopyOnAarrayList 底层分析,怎么通过写时复制副本,提升并发性能?

32.ConcurrentLinkedQueue 底层分析,CAS 无锁化操作提升并发性能?

33.ConcurrentHashMap详解,底层怎么通过分段锁提升并发性能?

34.LinkedBlockedQueue 阻塞队列怎么通过ReentrantLock和Condition实现?

35.ArrayBlockedQueued 阻塞队列实现思路竟然和LinkedBlockedQueue一样?

36.DelayQueue 底层源码剖析,延时队列怎么实现?

37.SynchronousQueue底层原理解析

JAVA并发专题《飞升篇》线程池底层深度剖析

38. 什么是线程池?看看JDK提供了哪些默认的线程池?底层竟然都是基于ThreadPoolExecutor的?

39.ThreadPoolExecutor 构造函数有哪些参数?这些参数分别表示什么意思?

40.内部有哪些变量,怎么表示线程池状态和线程数,看看道格.李大神是怎么设计的?

41. ThreadPoolExecutor execute执行流程?怎么进行任务提交的?addWorker方法干了啥?什么是workder?

42. ThreadPoolExecutor execute执行流程?何时将任务提交到阻塞队列? 阻塞队列满会发生什么?

43. ThreadPoolExecutor 中的Worker是如何执行提交到线程池的任务的?多余Worker怎么在超出空闲时间后被干掉的?

44. ThreadPoolExecutor shutdown、shutdownNow内部核心流程

45. 再回头看看为啥不推荐Executors提供几种线程池?

46. ThreadPoolExecutor线程池篇总结

  • 14
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: Java中的对象对象实例在内存中的一部分,包含了用于控制对象状态的信息。对象的内容包括标记字、类型指针、数组长度和锁信息等。标记字用于表示对象是否被锁定、是否已经被回收等状态信息。类型指针则指向对象的类元数据信息,包括类的类型、字段信息和方法信息等。数组长度记录了数组对象的长度信息。锁信息包括了对象的同步状态,用于实现Java中的同步机制。通过监控对象,我们可以了解对象在内存中的状态信息,为性能分析和问题排查提供帮助。 ### 回答2: Java中的monitor对象是用于实现线程同步和互斥的一种机制。Monitor对象实际上是存在于每个Java对象对象中的一个标志位,用来标示该对象是否被锁定,以及锁的相关信息。 在Java虚拟机中,每个对象对象占据8字节的空间,其中2字节存储monitor对象,用于记录锁的状态和计数器等信息。在Java的并发编程中,对一个对象进行同步的操作就是通过获取和释放这个monitor对象来实现的。 当一个线程想要获取一个对象的锁时,它首先会检查该对象monitor对象是否被其他线程锁定。如果对象没有被锁定,当前线程就会尝试获取这个锁,并将monitor对象标记为被当前线程所拥有。此时,其他任何线程都无法获取该对象的锁,直到当前线程释放了这个锁。 如果一个对象已经被锁定,那么当前线程就会进入到对象的等待队列中,直到锁被释放。同时,这个monitor对象会记录锁的计数器,用于区分同一个线程多次获取锁的次数。只有当锁的计数器归零时,锁才会完全释放,其他线程才能够获取到这个对象的锁。 监视器对象在Java多线程编程中起着非常重要的作用,它提供了线程间的互斥和同步机制,使得多个线程可以安全地共享对象。借助于monitor对象,可以有效地避免多个线程同时访问共享资源导致的并发问题,从而保证程序的正确性和安全性。 ### 回答3: Java 中的 monitor 对象是指每个对象在内存中的一块特定区域,用于存储对象的元数据信息和同步状态。它包含了以下几个重要的字段: 1. Mark Word(标记字段):用于存储对象的哈希码、锁状态、GC 分代年龄、锁标志等信息。它是对象的标志,用于判断对象的锁状态。 2. Klass Pointer(类型指针):指向对象的类元数据信息,包括类的方法、字段、父类、实现的接口等信息。通过这个指针,可以得到对象的具体类型以及相关的类信息。 3. Array Length(数组长度):仅在表示数组对象时使用,用于存储数组的长度。 4. Instance Data(实例数据):用于存储对象的实例变量。根据对象的类型,实例数据的存储方式可能会有所不同。 5. Padding(填充字段):为了对象在内存中对齐而添加的填充字段,保证对象的内存布局符合 JVM 的要求。 monitor 对象的主要作用是管理对象的同步状态,提供了对对象的并发访问控制。在 Java 中,每个对象都与一个 monitor关联,它用于实现 synchronized 关键字的功能。当一个线程试图获取对象的锁时,monitor 对象的锁标志会被设置为锁定状态,其他线程无法获取该对象的锁,必须等待锁的释放。 除了锁机制,monitor 对象还用于实现线程的等待/通知机制。通过调用 Object 类中的 wait()、notify() 和 notifyAll() 方法,线程可以进入等待状态并释放对象的锁,其他线程可以通过 notify() 或 notifyAll() 方法来唤醒等待的线程。 总之,Java 中的 monitor 对象是用于存储对象的元数据信息和同步状态的特定区域,它在多线程环境中实现了线程间的同步和互斥。它是实现 synchronized 关键字以及等待/通知机制的基础。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值