📫作者简介:小明java问道之路,2022年度博客之星全国TOP3,专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化,文章内容兼具广度、深度、大厂技术方案,对待技术喜欢推理加验证,就职于知名金融公司后端高级工程师。
📫 热衷分享,喜欢原创~ 关注我会给你带来一些不一样的认知和成长。
🏆 2022博客之星TOP3 | CSDN博客专家 | 后端领域优质创作者 | CSDN内容合伙人
🏆 InfoQ(极客邦)签约作者、阿里云专家 | 签约博主、51CTO专家 | TOP红人、华为云享专家
🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~
🍅 文末获取联系 🍅 👇🏻 精彩专栏推荐订阅收藏 👇🏻
专栏系列(点击解锁)
学习路线(点击解锁)
知识定位
全面讲解MySQL知识与企业级MySQL实战 🔥计算机底层原理🔥
Synchronized用的锁是存在java的对象头里面的。一个对象被new出来之后包含四个部分:对象头、类型指针、实例数据、对齐填充。
对象头 Mark Word 存储了对象的 hashCode、GC信息、锁信息三部分,这部分占8字节。每个对象都有个 monitor 对象,加锁就是在竞争 monitor 对象,代码块加锁是在前后分别加上 monitorenter 和 monitorexit 指令来实现的,方法加锁是通过一个标记位来判断的。
synchronized是一种对象锁,是可重入的,但是不可中断的(这个不可中断指的是在阻塞队列中排队是不可中断),非公平的锁。
加了synchronized后,在字节码会有二个指令monitorenter、monitorexit。
每个对象都是一个监视器锁(monitor),当monitor被占用时就会处于锁定状态,线程执行 monitorenter指令时尝试获取monitor的所有权,如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor 的所有者;如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1;如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝 试获取monitor的所有权。
执行monitorexit的线程必须是objectref所对应的monitor的所有者,指令执行时,monitor的进入数减 1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去 获取这个 monitor 的所有权。
Monitor 对象存在于每个 Java对象的对象头里(存储的指针的指向)。synchronized重量级锁是依赖对象内部的Monitor锁来实现的,而Monitor又依赖操作系统的MutexLock(互斥锁)来实现的,所以重量级锁也称为互斥锁。
Java对象头一般占有2个机器码(在32位虚拟机中,1个机器码等于4字节,也就是32bit, 在64位虚拟机中,1个机器码是8个字节,也就是64bit)
对象头主要由如下两个部分组成:Mark Word:存储的是 对象的 hashCode、锁信息、或分代年龄、GC标注等信息。Class Metadata Address: 存储对象所属类(元数据) 的指针,JVM通过这个确定这个对象属于哪个类。
实例数据:存放类的属性数据信息,包括父类的属性信息
对齐填充:由于虚拟机要求 对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。
Monitor实现的锁是互斥锁也称为重量级锁,重量级锁有一个缺点是线程开销很大。原因是当系统检测到锁是重量级锁后,会把等待的想要获得锁的线程进行阻塞,被阻塞的线程不会消耗CPU,但是阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态切换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还长。
synchronized一共有四种锁状态,锁的级别由低到高分别是:无锁状态->偏向锁状态->轻量级锁状态->重量级锁状态。这几个状态会随着锁的竞争情况逐渐升级(只能升级,不能降级)。
偏向锁偏向锁不是一把锁,而是代表了当前synchronized 锁状态。 当只有一个来线程加锁的时候,此时synchronized 锁就会变成偏向锁,偏向锁代表这个锁偏向这个线程,就是说当这个线程再次来加锁的时候,不需要再向操作系统申请资源,而是很快就能获取到锁,减少了申请锁的开销。
轻量级锁是当有多个线程参与到偏向锁的竞争中时,会先判断 markword 中的线程ID与这个线程是否一致,如果不一致,则会立即撤销偏向锁,升级为轻量级锁。
轻量级锁在代码进入同步块前,如果该同步块没有被锁定(即锁的标志位为“01”),那么JVM在将当前线程的栈帧中,创建一个 LockRecord(锁记录LR),并将锁对象头中的 markWord 信息复制到锁记录(LR)中,这个官方称为 Displaced Mard Word,然后线程尝试使用 CAS 将对象头中的 MarkWord 替换为指向锁记录(LR)的指针。
如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位(Mark Word的最后2bit)将转变为“00”,即表示此对象处于轻量级锁状态。
如果失败,表示有其他线程竞争锁,当前线程便尝试使用自旋来获取锁。如果在自旋一定次数后仍为获得锁,那么轻量级锁将会升级成重量级锁。
synchronized用的锁是存在java的对象头里面的,加了synchronized后,在字节码会有二个指令monitorenter、monitorexit。
Monitor 对象存在于每个 Java对象的对象头里(存储的指针的指向)。
synchronized重量级锁是依赖对象内部的Monitor锁来实现的,而Monitor又依赖操作系统的MutexLock(互斥锁)来实现的。
synchronized一共有四种锁状态,锁的级别由低到高分别是:无锁状态->偏向锁状态->轻量级锁状态->重量级锁状态。