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 动态代理

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Prompt Engineering,提示工程

什么是提示工程? 提示工程也叫【指令工程】。 Prompt发送给大模型的指令。比如[讲个笑话]、[用Python编个贪吃蛇游戏]、[给男/女朋友写情书]等看起来简单,但上手简单精通难 [Propmpt]是AGI时代的[编程语言][Propmpt]是AGI时代的[软件工程][提示工程]是…

Docker与Linux容器:“探索容器化技术的奥秘”

目录 一、Docker概述 二、容器技术的起源: 三、Linux容器 四、Docker的出现 五、Docker容器特点: 六、Docker三大概念: 容器: 镜像: 仓库: 七、Docker容器常用命令 一、Docker概述 在云原生时代&…

漫谈车辆诊断之DTC的状态位

搞车辆诊断测试的,离不开对DTC的状态位的测试 DTC的状态位是8个bit,每个bit代表不同的意思,每个bit置1或者置0都要满足一定的条件 初学者,很容易被这个搞得头很大 今天我们就来详细拆解一下,扫除你心中的疑惑 我们以…

8个拿来即用的Python自动化脚本!

每天你都可能会执行许多重复的任务,例如阅读新闻、发邮件、查看天气、清理文件夹等等,使用自动化脚本,就无需手动一次又一次地完成这些任务,非常方便。而在某种程度上,Python 就是自动化的代名词。 今天分享 8 个非常…

发布自己的Docker镜像到DockerHub

学会了Dockerfile生成Docker image 之后,如何上传自己的镜像到 DockerHub呢?下面我以自己制作的 bs-cqhttp 镜像为例,演示一下如何将自己的镜像发布到 Docker 仓库。 1 生成自己的 Docker 镜像 1.1 实例镜像用到的文件 图1 实例镜像制作用到…

stm32f103zet6_ADC_中断_2

1基本配置请查看 ADC_1 1stm32cubeMX配置 与ADC-1 区别在于配置了NVIC 2代码设置 回调函数 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) 这个回调函数在ADC正常转换完成后被调用。当你启动一个ADC转换(使用HAL_ADC_Start_IT()或类似函数&#x…

【数据结构(邓俊辉)学习笔记】绪论04——算法分析

文章目录 0. 前言1. 算法分析2.级数2.1基本形式2.2 收敛级数 3.循环 vs 级数4.示例 0. 前言 通过以基本计算模型作为参照,并且以大O记号的形式在上面添加适当刻度,已经建立一套对DSA进行分析的完整工具和体系。不清楚的可以看看复杂度度量 、复杂度分析…

git 命令怎么回退到指定的某个提交 commit hash 并推送远程分支?

问题 如下图,我要回退到 【002】Babel 的编译流程 这一次提交 解决 1、先执行下面命令,输出日志,主要就是拿到提交 commit 的 hash,上图红框即可 git log或者 vscode 里面直接右击,copy sha 2、执行下面命令回退 g…

【Linux】如何进行用户之间的切换——指令su

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …

【Linux系统编程】基础指令(二)

💞💞 前言 hello hello~ ,这里是大耳朵土土垚~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹 💥个人主页&#x…

【OceanBase诊断调优】——hpet(高精度时钟源)引起的CPU高问题排查

最近总结一些诊断OCeanBase的一些经验,出一个【OceanBase诊断调优】专题出来,也欢迎大家贡献自己的诊断OceanBase的方法。 1. 前言 昨天在问答区帮忙排查一个用户CPU高的问题,帖子链接:《刚刚新安装的OceanBase集群,…

代码随想录算法训练营第三十六天| 435.无重叠区间、763.划分字母区间、56.合并区间

系列文章目录 目录 系列文章目录435. 无重叠区间贪心算法按左边界排序(与【452. 用最少数量的箭引爆气球】思路差不多) 763.划分字母区间贪心算法①将字符串映射到哈希数组,用二维数组记录每个字母存在的区间, 转化为重叠区间问题…

手搓单链表(无哨兵位)(C语言)

目录 SLT.h SLT.c SLTtest.c 测试示例 单链表优劣分析 SLT.h #pragma once#include <stdio.h> #include <assert.h> #include <stdlib.h>typedef int SLTDataType;typedef struct SListNode {SLTDataType data;struct SListNode* next; }SLTNode;//打印…

数据治理和数据管理 傻傻分不清楚?

互联网时代&#xff0c;数据&#xff0c;这一无形资产&#xff0c;已成为现代企业的核心竞争力。如何高效地管理和利用数据&#xff0c;成为企业关注的焦点。在这个过程中&#xff0c;数据治理&#xff08;Data Governance&#xff09;和数据管理&#xff08;Data Management&a…

1分钟掌握 Python 函数参数

任何编程语言函数都是非常重要的一部分&#xff0c;而在进行函数调用时&#xff0c;了解函数的参数传递方式是非常有必要的。Python中支持哪些传参方式呢&#xff1f; Python中的传参方式是比较灵活的&#xff0c;主要包括以下六种&#xff1a; 按照位置传参按照关键字传参默…

【算法基础实验】图论-构建无向图

构建无向图 前提 JAVA实验环境 理论 无向图的数据结构为邻接表数组&#xff0c;每个数组中保存一个Bag抽象数据类型&#xff08;Bag类型需要专门讲解&#xff09; 实验数据 我们的实验数据是13个节点和13条边组成的无向图&#xff0c;由一个txt文件来保存&#xff0c;本…

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

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

森林消防泵柱塞泵工作原理深度解析——恒峰智慧科技

森林是地球上重要的生态系统&#xff0c;而森林火灾则是这一生态系统面临的主要威胁之一。为了有效应对森林火灾&#xff0c;森林消防泵成为了不可或缺的灭火工具。其中&#xff0c;柱塞泵作为森林消防泵的核心部件&#xff0c;其工作原理的理解对于提高森林消防效率具有重要意…

Java面试八股文-2024

面试指南 TMD&#xff0c;一个后端为什么要了解那么多的知识&#xff0c;真是服了。啥啥都得了解 MySQL MySQL索引可能在以下几种情况下失效&#xff1a; 不遵循最左匹配原则&#xff1a;在联合索引中&#xff0c;如果没有使用索引的最左前缀&#xff0c;即查询条件中没有包含…

Javascript 插值搜索与二分搜索

插值搜索和二分搜索都是在有序数组中查找目标元素的算法。它们之间的核心区别在于确定中间元素的方式。 1、二分搜索&#xff08;Binary Search&#xff09;&#xff1a;二分搜索是一种通过将目标值与数组中间元素进行比较&#xff0c;然后根据比较结果缩小搜索范围的算…