你是哪家的锁,这么膨胀?

简介: 大家好,我是指北君。PS:最近又赶上跳槽的高峰期,好多粉丝,都问我要有没有最新面试题,我连日加班好多天,终于整理好了,16000+ 道,295多份,多份面试题大全,我会持续更新中,马上就会整理更多!【文末有领取方式】

在面试的时候,最会被问到的多线程问题就是 synchronized,如果还只会回答 monitorenter 和 monitorexit 那就有可能通不过面试,除了 monitorenter,还可以和面试官聊聊 synchronized 的锁膨胀。


初识


synchronized 可以加在方法和类上面,作用于类和对象。下面代码中列出了 synchronized 的用法。

30.png

synchronized 大家都知道是用 monitorenter 和 monitorexit 两个指令锁住同步块的。

那么 synchronized 是怎么膨胀的呢?为什么会膨胀呢?

先从 JVM 内存开始讲起,对象在被实例化后,是存放在堆内存中的,它由 3 部分组成:

  1. 对象头:存放对象运行时的状态的信息、指向该对象所属 Class 的元数据的指针。
  2. 实例数据:存放对象的属性数据信息,包括父类的信息。
  3. 对齐填充字节:由于虚拟机要求对象的大小必须是 8 字节的整数倍。不是必须存在,仅仅是为了字节对齐。

其中对象头里面包含了 Mark Word(标记字段)和 Class Pointer(类型指针)


31.jpgMark 1.Word 默认的存储对象的 hashcode、分代年龄、是否偏向锁、锁标识位的信息,它在运行期间的存储内容会随着锁的变化而变化。


32.png

  1. Class Pointer(类型指针):对象指向类的元数据的指针,虚拟机通过这个指针来确定对象是哪一个类的实例。


锁膨胀


偏向锁、轻量级锁、重量级锁、自旋锁,这些都是Synchronzied的锁的实现。Synchrozied会根据不同的场景选择不同的锁,我们只使用Synchronzied,不用关心它具体使用的哪个锁。

偏向锁

在java 程序中,大多数情况不存在多个线程同时竞争锁,往往都是同一个线程多次获得同一个锁。

当只有一个线程在竞争锁的时候,在线程获取到锁后,将进入偏向模式,程序会将对象的头的前 23 个字节用 CAS 的方式存储线程 ID。下次有线程竞争锁,只需要比较对象头中的线程 ID 是不是和此时获取到锁的线程 ID 相同。如果相同线程就直接进入同步代码块,不需要 CAS 竞争锁。


33.jpg


有另外的线程在竞争锁的时候,持有偏向锁的线程才会释放锁,持有偏向锁的线程不会主动释放偏向锁。偏向锁的撤销,是在没有字节码执行的时候进行的。首先会暂停偏向锁的线程,判断锁对象是否被锁住。撤销偏向锁后恢复成无锁或者是轻量级锁。


轻量级锁


当有另外的线程在竞争偏向锁的时候并且竞争失败了,偏向锁就会膨胀为轻量级锁,其他的线程会通过自旋的方式尝试获取锁。

JVM 会在当前线程的栈帧中创建一个叫做锁记录(Lock Record)的空间,将锁对象的 Mark Word 复制进去。这个官方称为 Displaced Mard Word。然后 JVM 将使用 CAS 操作尝试将锁对象的Mark Word 更新为指向 Lock Record 的指针。如果更新成功,锁标识位就成为 00,此时为轻量级锁。

34.jpg

重量级锁

从上面的表格中就指出重量级锁的对象头里面存储的是指向 monitor 的指针,那 monitor 是什么呢?

monitor 又称为管程,Java 中由 ObjectMonitor 实现。当线程要将对象加锁的时候,对象会创建一个monitor。


35.jpg

ObjectMonitor 主要的字段有:

  1. owner:就是当前加锁的线程
  2. waitSet:就是 owner的线程调用了 wait() 方法,就进入这个里面
  3. entryList:加锁失败的线程阻塞在这个里面
  4. recursions:锁的重入次数
  5. count:用来记录是不是有对象加锁:0.当前对象没有线程加锁,1. 当前对象有线程加锁

从轻量级锁升级到重量级锁的时候,对象头 Mark Word 存储已经变成了指向 Monitor 的指针。线程可以通过这个指针找到 ObjectMonitor,放入 entryList 等待重量级锁释放后竞争。entryList 中的线程 CAS 尝试更新 count = 1,当更新成功后将 owner 设置为当前的线程。当 owner 的线程调用了 wait() 方法,线程就会释放锁,进入 waitSet 中。这个时候 count = 1,owner = null,entryList 的线程可以再次竞争锁。


36.jpg


总结


  1. synchronized 不管是加在类上还是方法上,如果作用在类上,这个类的所有对象都是同一把锁,
  2. 锁膨胀时不可以降级的

我是指北君,操千曲而后晓声,观千剑而后识器。感谢各位人才的:点赞、收藏和评论,我们下期更精彩!

面试大全包括:包括 Java 集合、JVM、多线程、并发编程、设计模式、SpringBoot、SpringCloud、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat、Python、HTML、CSS、Vue、React、JavaScript、Android 大数据、阿里巴巴等大厂面试题等、等技术栈!

领取方式:扫描下方公众号【Java技术指北】回复【面试大全】即可获取

37.jpg

相关文章
|
5天前
|
存储 关系型数据库 分布式数据库
PostgreSQL 18 发布,快来 PolarDB 尝鲜!
PostgreSQL 18 发布,PolarDB for PostgreSQL 全面兼容。新版本支持异步I/O、UUIDv7、虚拟生成列、逻辑复制增强及OAuth认证,显著提升性能与安全。PolarDB-PG 18 支持存算分离架构,融合海量弹性存储与极致计算性能,搭配丰富插件生态,为企业提供高效、稳定、灵活的云数据库解决方案,助力企业数字化转型如虎添翼!
|
16天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1315 5
|
2天前
|
监控 JavaScript Java
基于大模型技术的反欺诈知识问答系统
随着互联网与金融科技发展,网络欺诈频发,构建高效反欺诈平台成为迫切需求。本文基于Java、Vue.js、Spring Boot与MySQL技术,设计实现集欺诈识别、宣传教育、用户互动于一体的反欺诈系统,提升公众防范意识,助力企业合规与用户权益保护。
|
15天前
|
机器学习/深度学习 人工智能 前端开发
通义DeepResearch全面开源!同步分享可落地的高阶Agent构建方法论
通义研究团队开源发布通义 DeepResearch —— 首个在性能上可与 OpenAI DeepResearch 相媲美、并在多项权威基准测试中取得领先表现的全开源 Web Agent。
1366 87
|
2天前
|
JavaScript Java 大数据
基于JavaWeb的销售管理系统设计系统
本系统基于Java、MySQL、Spring Boot与Vue.js技术,构建高效、可扩展的销售管理平台,实现客户、订单、数据可视化等全流程自动化管理,提升企业运营效率与决策能力。
|
4天前
|
弹性计算 安全 数据安全/隐私保护
2025年阿里云域名备案流程(新手图文详细流程)
本文图文详解阿里云账号注册、服务器租赁、域名购买及备案全流程,涵盖企业实名认证、信息模板创建、域名备案提交与管局审核等关键步骤,助您快速完成网站上线前的准备工作。
198 82
2025年阿里云域名备案流程(新手图文详细流程)