java锁升级过程过程详解

 更新时间:2021年08月27日 15:59:51   作者:我要当无敌内卷人  
这篇文章主要介绍了Java锁升级的实现过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1.说到锁升级的过程,我们就得说一下对象头

对象头

java对象保存在内存中,由3个部分组成:

1. 对象头

2. 实例数据

3. 对齐填充字节

4. 如果是数组还包含数组长度

对象头的存在形式

让我们先看看图,主要来说一下 Mark Word

  • markword 8bytes
  • class pointer - 指向对象所属的class (一般是4bytes)
  • instance data - 成员变量
  • padding - 8字节对齐

Mark Word里都有啥

hashcode

GC

为了让你们更好理解我先放一张图

此处,有几点要注意:

  • 如果对象没有重写hashcode方法,那么默认是调用os::random产生hashcode,可以通过System.identityHashCode获取;os::random产生hashcode的规则为:next_rand = (16807seed) mod (2*31-1),因此可以使用31位存储;另外一旦生成了hashcode,JVM会将其记录在markword中;
  • GC年龄采用4位bit存储,最大为15,例如MaxTenuringThreshold参数默认值就是15;
  • 当处于轻量级锁、重量级锁时,记录的对象指针,根据JVM的说明,此时认为指针仍然是64位,最低两位假定为0;当处于偏向锁时,记录的为获得偏向锁的线程指针,该指针也是64位;

这里呢,是讲解的锁升级所以就重点看一下后两位,锁状态的判断就是看后两位的状态,无锁和偏向锁是看倒数第三位的状态

接下来让我们看看锁升级的过程

专业版解释

1、当没有被当做锁的时候,这就是个普通对象,锁标志位为01,是否偏向锁为0

2、当对象被当做同步锁时,一个线程A抢到锁时,锁标志位依然是01,是否偏向锁为1,前23位记录A线程的线程ID,此时锁升级为偏向锁

3、当线程A再次试图来获得锁时,JVM发现同步锁对象的标志位是01,是否偏向锁是1,也就是偏向状态,Mark Word中记录的线程id就是线程A自己的id,表示线程A已经获得了这个偏向锁,可以执行同步锁的代码,这也是偏向锁的意义

4、当一个线程B尝试获取锁,JVM发现当前的锁处于偏向状态,并且现场ID不是B线程的ID,那么线程B会先用CAS将线程id改为自己的,这里是有可能成功的,因为A线程一般不会释放偏向锁。如果失败,则执行5

5、偏向锁抢锁失败,则说明当前锁存在一定的竞争,偏向锁就升级为轻量级锁。JVM会在当前线程的现场栈中开辟一块单独的空间,里面保存指向对象锁Mark Word的指针,同时在对象锁MarkWord中保存指向这片空间的指针。上面的保存都是CAS操作,如果竞争成功,代表线程B抢到了锁,可以执行同步代码。如果抢锁失败,则继续执行6

6、轻量级锁抢锁失败,则JVM会使用自旋锁,自旋锁并非是一个锁,则是一个循环操作,不断的尝试获取锁。从JDK1.7开始,自旋锁默认开启,自旋次数由JVM决定。如果抢锁成功,则执行同步代码;如果抢锁失败,则执行7

7、自旋锁重试之后仍然未抢到锁,同步锁会升级至重量级锁,锁标志位改为10,在这个状态下,未抢到锁的线程都会被阻塞,由Monitor来管理,并会有线程的park与unpark,因为这个存在用户态和内核态的转换,比较消耗资源,故名重量级锁

详情请看:https://blog.csdn.net/wyb_gg/article/details/107518521

我通过马士兵老师讲的带味道的栗子大致懂了这个过程(菜鸟版理解)

首先呢,小马去上厕所噗噗噗,但是这个厕所很特殊,门上是没有锁的(无锁状态)

小马觉得这不太安全啊,于是就想了个办法,上厕所噗噗噗的时候先贴上自己的名字,这样是不是就不会遇到尴尬的事(偏向锁)

但是这样还是不好,要是方圆百里只有这一个厕所,翠花和小李都想上厕所怎么办,这时候就发生了锁竞争,他们会通过一个叫CAS来抢这个厕所,他们中有可能成功,把自己的名字贴到厕所门上,那如果没成功呢???

没成功就会升级成轻量级锁,jvm会在当前线程的现场栈开辟一块空间,让翠花和小李在那里转圈圈的抢着谁上厕所(也叫自旋)也是通过CAS来实现的, 自旋的次数是10次以上,或者CPU核数的一半(JDK1.7开始,自旋锁默认开启,自旋次数由JVM决定) 那如果又失败了呢! 俩孩子快拉裤兜子了!!!!!!

这时候就会升级成重量级锁,重量级这个词一听就不一般,JVM说:我头快秃了,干不了了。所以,我们的重量级锁是os老大哥管理的

注:

1.CAS中呢,底层是lock cmpxchg(大家不会的话可以自行百度)CAS也有很多问题:就像ABA啥的,这里就不多bb了

2.那么有的小小猿就会问了,啥时候变成匿名对象呢?是4s以后才会加上偏向锁,变成匿名对象滴,那么咋取消呢,-XX:-UseBiasedLocking 或者去sleep

这就是我对锁升级的理解,如果有错误的话,还望指正

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • 详解JVM系列之对象的锁状态和同步

    详解JVM系列之对象的锁状态和同步

    锁和同步是java多线程编程中非常常见的使用场景。为了锁定多线程共享的对象,Java需要提供一定的机制来实现共享对象的锁定。当第二个线程进入同一个区域的时候,必须等待第一个线程解锁该对象。JVM是怎么做到的呢?快来一起看看吧。
    2021-06-06
  • Java实现添加、验证PDF数字签名的方法示例

    Java实现添加、验证PDF数字签名的方法示例

    在设置文档内容保护的方法中,除了对文档加密、添加水印外,应用数字签名也是一种有效防伪手段。本文就使用Java实现添加、验证PDF数字签名,感兴趣的可以了解一下
    2021-07-07
  • Java中的set集合是什么意思

    Java中的set集合是什么意思

    这篇文章主要介绍了Java中的set集合是什么意思,详细地讲解一下Collection集合中的另外一个分支——Set系列集合,需要的朋友可以参考下
    2022-05-05
  • Java管道流实现线程间通信过程解析

    Java管道流实现线程间通信过程解析

    这篇文章主要介绍了Java管道流实现线程间通信过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • 使用Spring Cache设置缓存条件操作

    使用Spring Cache设置缓存条件操作

    这篇文章主要介绍了使用Spring Cache设置缓存条件操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • spring是如何实现声明式事务的

    spring是如何实现声明式事务的

    这篇文章主要介绍了spring是如何实现声明式事务的,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • springboot jta atomikos实现分布式事物管理

    springboot jta atomikos实现分布式事物管理

    这篇文章主要介绍了springboot jta atomikos实现分布式事物管理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • Maven环境安装配置和新建项目介绍

    Maven环境安装配置和新建项目介绍

    这篇文章介绍了Maven环境安装配置和新建项目介绍,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • Spring Boot集成mongodb数据库过程解析

    Spring Boot集成mongodb数据库过程解析

    这篇文章主要介绍了Spring Boot集成mongodb数据库过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • Java面试之高级特性基础总结

    Java面试之高级特性基础总结

    这篇文章主要为大家详细介绍了10个Java高级特性基础相关的问题,也是大家面试中常常会遇到的问题。文中的示例代讲解详细,感兴趣的可以了解一下
    2023-01-01

最新评论