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的代码块 之前会执行monitorente r指令,去申请monitor监视器的操作权,如果申请成功了,就相当于获取到了锁。

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

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

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

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

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

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

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

JAVA对象结构

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

java 复制代码
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并发专题 《筑基篇》

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并发专题《结丹篇》

  1. 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 读写锁怎么表示?

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

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

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

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

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

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

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

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

37.SynchronousQueue底层原理解析

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

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

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

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

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

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

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

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

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

  6. ThreadPoolExecutor线程池篇总结

相关推荐
武子康14 分钟前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
豪宇刘1 小时前
MyBatis的面试题以及详细解答二
java·servlet·tomcat
秋恬意1 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
刘大辉在路上2 小时前
突发!!!GitLab停止为中国大陆、港澳地区提供服务,60天内需迁移账号否则将被删除
git·后端·gitlab·版本管理·源代码管理
FF在路上2 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进2 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
众拾达人3 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
皓木.3 小时前
Mybatis-Plus
java·开发语言
不良人天码星3 小时前
lombok插件不生效
java·开发语言·intellij-idea
测试老哥3 小时前
外包干了两年,技术退步明显。。。。
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展