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的…

【Java GUI】人机对弈五子棋

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

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

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

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

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

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

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

maldev tricks在注册表中存储 payload

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

十七、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实现细节初始化注册监听编译完成事件启动服务监听文件代码变化服务端发送消息客户端收到消息热更新文件请求热更新代码替换 问题思考 前言 刷新分为两种:一种是页面刷新,不保留页面状态…

GPU深度学习环境搭建:Win10+CUDA 11.7+Pytorch1.13.1+Anaconda3+python3.10.9

1. 查看显卡驱动及对应cuda版本关系 1.1 显卡驱动和cuda版本信息查看方法 在命令行中输入【nvidia-smi】可以当前显卡驱动版本和cuda版本。 根据显示,显卡驱动版本为:Driver Version: 516.59,CUDA 的版本为:CUDA Version 11.7。 此处我们可以根据下面的表1 显卡驱动和c…

大模型咨询培训老师叶梓:利用知识图谱和Llama-Index增强大模型应用

大模型(LLMs)在自然语言处理领域取得了显著成就,但它们有时会产生不准确或不一致的信息,这种现象被称为“幻觉”。为了提高LLMs的准确性和可靠性,可以借助外部知识源,如知识图谱。那么我们如何通过Llama-In…

将阿里云中数据传输到其他超算服务器

目录 方法一:在阿里云中连接超算,然后使用rsync(速度慢) 方法2:rclone(速度很快,100G只花了大约20min) 方法一:在阿里云中连接超算,然后使用rsync/scp(速度慢&#xff0…

网贷大数据黑名单要多久才能变正常?

网贷大数据黑名单是指个人在网贷平台申请贷款时,因为信用记录较差而被列入黑名单,无法获得贷款或者贷款额度受到限制的情况。网贷大数据黑名单的具体时间因个人信用状况、所属平台政策以及银行审核标准不同而异,一般来说,需要一定…

mac: docker安装及其Command not found: docker

已经安装了docker desktop,没安装的 点击安装 傻瓜式安装即可 接着打开终端:好一个 Comand not found:docker 看我不把你整顿,解决如下: 如果你在 macOS 上安装了 Docker Desktop,但是终端无法识别 docker 命令&…