synchronized 之谜

序言

本文给大家介绍一下 synchronized 关键字的部分原理。

一、内存中的 Java 对象

class A {private String attr;
}

先引入一个问题:上面类 A 有一个属性 attr。当类 A 实例化之后的对象在内存中是如何表示的呢?

workspace.png

在内存中,Java 对象由三部分组成。其中包括对象头(Header)、实例数据(Instance Data)和对齐填充 (Padding)等部分。

  1. 对象头(Object Header):对象头存储了对象的元数据信息,比如哈希码、锁状态、垃圾回收标记等。对象头的大小在 32 位和 64 位的 JVM 上可能会有所不同,通常占用 8 字节或更多的空间。
  2. 实例数据(Instance Data):实例数据是对象的成员变量(字段)的实际存储区域。每个字段根据其类型占用不同的内存空间。例如,一个 int 类型的字段占用 4 字节,一个对象引用占用 4 字节(在 32 位 JVM 上)或 8 字节(在 64 位 JVM 上)。
  3. 对齐填充(Padding):由于硬件要求数据在内存中的地址是对齐的,因此在实例数据和对象头之间可能存在一些填充字节,以保证对象的起始地址是对齐的。填充的大小通常是对象头和实例数据大小的倍数,以满足对齐要求。

二、Java 对象头

对象头包含了两部分,分别是运行时元数据(Mark Word)和类型指针(Klass Word)。

workspace (1).png

在 32 位的虚拟机中,对象头占 64 位。其中 Mark Word 占 32 位,Klass Word 占 32 位。

workspace (2).png

在 64 位的虚拟机中,对象头占 128 位。其中 Mark Word 占 64 位,Klass Word 占 64 位。
接下来我们以 64 位的虚拟机为例。

三、Mark Word

对象头的 Mark Word 存储了对象的元信息,其中包括了对象的锁状态、GC(垃圾回收)相关信息等。 Mark Word 中通常用来表示对象的锁状态的部分称为锁标志位 (Lock Word),它包含了对象的锁状态、线程 ID 等信息。

通常情况下,锁标志位可以有以下几种状态:

  1. 无锁状态:对象尚未被锁定,可以被任意线程访问。
  2. 偏向锁状态:对象已经被某个线程锁定,但是尚未涉及竞争。在这种状态下,MarkWord 中会记录拥有锁的线程 ID,并且对象的锁标志位中会设置偏向锁标志。
  3. 轻量级锁状态:多个线程竞争同一个锁,但尚未涉及到真正的阻塞,因此采用了一种轻量级的锁机制来进行竞争。在这种状态下,MarkWord 中会记录锁的指针,用于指向锁记录(Lock Record),并且对象的锁标志位中会设置轻量级锁标志。
  4. 重量级锁状态:当轻量级锁竞争不过,会升级为重量级锁,这时会涉及到阻塞和唤醒线程的操作。在这种状态下,MarkWord 中不再存储锁记录的指针,而是直接指向锁对象,并且对象的锁标志位中会设置重量级锁标志。
  5. GC 标志位:有些 JVM 实现中,MarkWord 还可能包含 GC 相关的标志位,用于标记对象是否被回收等信息。

3.1 无锁状态的 Mark Word

workspace.png

上图是 64 位虚拟机中无锁状态的 Mark Word。表示无锁是 Mark Word 的后三位(Mark Word 后三位 001 表示无锁),即:baised_lock 标志位是 0,Mark Word 最后两位是 01。

3.2 偏向锁状态的 Mark Word

workspace (1).png

上图是 64 位虚拟机中偏向锁状态的 Mark Word。表示偏向锁是 Mark Word 的后三位(后三位 101 表示偏向锁),即:baised_lock 标志位是 1,Mark Word 最后两位是 01。

3.3 轻量级锁状态的 Mark Word

workspace.png

上图是 64 位虚拟机中轻量级锁状态的 Mark Word。后两位 00 表示轻量级锁。

3.4 重量级锁状态的 Mark Word

workspace.png

上图是 64 位虚拟机中重量级锁状态的 Mark Word。后两位 10 表示轻量级锁。

3.5 GC 标志位的 Mark Word

workspace (1).png
上图是 64 位虚拟机中 GC 标志位的 Mark Word。后两位 11 表示 GC 标志位。

四、synchronized 工作流程

class A {private String attr;public void setAttr(String attr) {// 使用 synchronized 加锁synchronized (this) {this.attr = attr;}}
}

在上述代码中,setAttr() 方法中使用了 synchronized,其中锁住的是当前对象 A。

  1. 当使用 synchronized 锁住对象 A ,对象 A 未被使用时,对象 A 的 Mark Word 依旧没有改变(即 Mark Word 后三位是 001,无锁状态)。
  2. 当第一次有一个线程去访问对象 A 时,此时 Mark Word 的锁标志位会变成 101(即表示对象现在使用的偏向锁)。
  3. 若后面有少量其他线程也去获取对象 A 的锁,对象 A 会先撤销偏向锁,偏向锁撤销成功则尝试采用 CAS 加上轻量级锁,轻量级锁加锁成功则将 Mark Word 的锁标志位变成 00(即表示对象现在使用的轻量级锁)
  4. 若后面有更多的线程前来争抢对象 A 的锁,其他未抢到锁的线程就会发生 CAS 自旋。自旋超时之后,系统便判断当前对于该锁的竞争非常激烈,将会撤销轻量级锁,然后加上重量级锁,并将锁标志位相应地更新为重量级锁即 10

在这里插入图片描述

通过上面的分析,我们知道 synchronized 在底层存在一种锁升级机制而不是使用一种固定的锁。这种锁升级机制是根据不同的并发度使用不同的加锁方式(这也是 Java 团队对 synchronized 的优化)。

当我们使用 synchronized 关键字时,一般就认为该部分代码会在多线程环境中执行。如果对象从无锁状态一步一步升级会浪费性能。所以,通常 JVM 会开启偏向锁,即对象创建之后 Mark Word 的后三位是 101 而不是 001。

往期推荐

  1. 为什么 MySQL 单表数据量最好别超过 2000w
  2. ConcurrentHashMap 源码分析(一)
  3. IoC 思想简单而深邃
  4. ThreadLocal
  5. JDK 动态代理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/3716.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024年03月CCF-GESP编程能力等级认证Python编程二级真题解析

本文收录于专栏《Python等级认证CCF-GESP真题解析》,专栏总目录:点这里,订阅后可阅读专栏内所有文章。 一、单选题(共15题,共30分) 第1题 小杨的父母最近刚刚给他买了一块华为手表,他说手表上跑的是鸿蒙,这个鸿蒙是? A.小程序 B.计时器 C.操作系统 D.神话人物 答案…

【Linux驱动层】iTOP-RK3568学习之路(三):字符设备驱动框架

一、总体框架图 二、字符设备相关函数 静态申请设备号 register_chrdev_region 函数原型:register_chrdev_region(dev_t from, unsigned count, const char *name) 函数作用:静态申请设备号,可以一次性申请多个连续的号,count指定…

电阻理论基础

电流的形成是电荷运动,电子方向相反,标量 电压:电势有参考点,是一个相对量 电阻的值不取决于电压和电流的, Ra表示标准电阻 R表示任意温度的电阻

Transformer step by step--Positional Embedding 和 Word Embedding

Transformer step by step往期文章: Transformer step by step--层归一化和批量归一化 要把Transformer中的Embedding说清楚,那就要说清楚Positional Embedding和Word Embedding。至于为什么有这两个Embedding,我们不妨看一眼Transformer的…

代码随想录算法训练营第三十八天| LeetCode509.斐波那契数、LeetCode70.爬楼梯、LeetCode746.使用最小花费爬楼梯

LeetCode 509 斐波那契数 题目链接:509. 斐波那契数 - 力扣(LeetCode) 【解题思路】 1.确定dp数组以及下标的含义 dp[i]的定义为:第i个数的斐波那契数值是dp[i] 2.确定递推公式 递推公式就是题目说的斐波那契值递推公式 3.确定…

【Java GUI】人机对弈五子棋

在学校的Java课程中,我们被分配了一项有趣的任务:开发一款能够实现人机对弈的五子棋游戏。为了更好地理解Java GUI的运用,并与大家分享学习心得,我将整个开发过程记录在这篇博客中。欢迎大家阅读并提供宝贵的意见和建议&#xff0…

微信小程序-------模板与配置

能够使用 WXML 模板语法渲染页面结构能够使用 WXSS 样式美化页面结构能够使用 app.json 对小程序进行全局性配置能够使用 page.json 对小程序页面进行个性化配置能够知道如何发起网络数据请求 一.WXML 模板语法 数据绑定 1. 数据绑定的基本原则 ① 在 data 中定义数据 ② 在…

文件分享新风尚,二维码生成器全功能解析

随着科技的飞速发展,二维码生成器已经成为我们日常生活中不可或缺的一部分。无论是支付、下载应用还是获取信息,二维码都以其便捷性和高效性赢得了广大用户的青睐。在这样的背景下,二维码生成器应运而生,不仅支持多文件生成二维码…

Android Studio修改“choose boot runtime for the IDE“后无法打开

在Android Studio中选择了"choose boot runtime for the IDE"的New后,会自动重启AS,然后就无法打开android studio了,打开直接报错,cause by如下 Unable to make field protected java.lang.Runnable java.awt.event.In…

[leetcode] 58. 最后一个单词的长度

文章目录 题目描述解题方法倒序遍历java代码复杂度分析 题目描述 给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 示例 1&#xff1a…

未来已来:解锁AGI的无限潜能与挑战

未来已来:解锁AGI的无限潜能与挑战 引言 假设你有一天醒来,发现你的智能手机不仅提醒你今天的日程,还把你昨晚做的那个奇怪的梦解释了一番,并建议你可能需要减少咖啡摄入量——这不是科幻电影的情节,而是人工通用智能…

maldev tricks在注册表中存储 payload

简介 注册表是 Windows 操作系统中一个重要的数据库,它包含 Windows 操作系统和应用程序的重要设置和选项。由于注册表的功能非常强大,因此注册表对于恶意程序来说是非常有利用价值的。 在 windows 注册表中存储二进制数据,这是一种常见的技…

每天一个数据分析题(二百九十六)

订单详情表是以每一笔订单的每一件商品为最小业务记录单位进行记录的,那么可能成为订单详情表的主键字段的是? A. 订单编号 B. 产品编号 C. 订单ID D. 订单编号产品编号 题目来源于CDA模拟题库 点击此处获取答案 cda数据分析考试:点击…

十七、Java网络编程(一)

1、Java网络编程的基本概念 1)网络编程的概念 Java作为一种与平台无关的语言,从一出现就与网络有关及其密切的关系,因为Java写的程序可以在网络上直接运行,使用Java,只需编写简单的代码就能实现强大的网络功能。下面将介绍几个与Java网络编程有关的概念。 2)TCP/IP协议概…

MVP+敏捷开发

MVP敏捷开发 1. 什么是敏捷开发? 敏捷开发是一种软件开发方法论,旨在通过迭代、自组织的团队和持续反馈,快速响应需求变化并交付高质量的软件。相较于传统的瀑布模型,敏捷开发强调灵活性、适应性和与客户的紧密合作。敏捷开发方…

qml和c++结合使用

目录 文章简介1. 创建qml工程2. 创建一个类和qml文件,修改main函数3. 函数说明:4. qml 文件间的调用5. 界面布局6. 代码举例 文章简介 初学qml用来记录qml的学习过程,方便后面归纳总结整理。 1. 创建qml工程 如下图,我使用的是…

【北京迅为】《iTOP龙芯2K1000开发指南》-第四部分 ubuntu开发环境搭建

龙芯2K1000处理器集成2个64位GS264处理器核,主频1GHz,以及各种系统IO接口,集高性能与高配置于一身。支持4G模块、GPS模块、千兆以太网、16GB固态硬盘、双路UART、四路USB、WIFI蓝牙二合一模块、MiniPCIE等接口、双路CAN总线、RS485总线&#…

光伏无人机:巡检无人机解决巡检难题

随着科技的飞速发展,无人机技术已经广泛应用于各个领域,其中光伏无人机在解决光伏电站巡检难题方面发挥了重要作用。光伏无人机以其高效、精准、安全的特点,为光伏电站的巡检工作带来了革命性的变革。 光伏电站通常位于广阔的户外场地&#x…

webpack热更新原理详解

文章目录 前言基础配置创建项目HMR配置 HMR交互概览HMR流程概述HMR实现细节初始化注册监听编译完成事件启动服务监听文件代码变化服务端发送消息客户端收到消息热更新文件请求热更新代码替换 问题思考 前言 刷新分为两种:一种是页面刷新,不保留页面状态…

二叉树的三种遍历方式非递归

## 二叉树的遍历需要把当前根节点和左右子树抽象成只有一个根节点和一个左子树,一个右子树的情况 ##套路 二叉树的遍历先序中序后序无非是根节点的处理顺序不同,三种遍历方式都有一个相同的逻辑流程,不管怎么样首先将左子树入栈,遍…