synchronized锁详解

2023-05-16

1.对象内存布局

在这里插入图片描述

对象头 MarkWord

对象头,8字节。包括了对象的hashCode、对象的分代年龄、锁标志位等。结构如下图所示:
在这里插入图片描述

类指针classPointer

对象指向它的类元素的指针。在不开启对象指针压缩的情况下是8字节。压缩后变为4字节,默认压缩。 通过命令:java
-XX:+PrintCommandLineFlags -version 查看classPointer是否开启压缩 该指针在32位JVM中的长度是32bit,在64位JVM中长度是64bit。Java对象的类数据保存在方法区。
对象可以通过此类指针获取类的位置。

数组长度

在数组情况下有此部分

实例数据 Instance Data

存放指向数据的指针,压缩情况下一般为4字节

对齐补充 Padding

用于对象在内存中占用的字节数不能被8整除的情况下,进行补充。

例题补充

1.Object o = new Object()在内存中占了多少字节?
markword 8字节, 因为java默认使用了calssPointer压缩,classpointer 4字节, padding 4字节 因此是16字节。
如果没开启classpointer默认压缩,markword 8字节,classpointer 8字节,padding 0字节,也是16字节。
2.User (int id,String name) User u = new User(1,‘张三’);占用多少字节 markword 8字节?
开启classPointer压缩 ,classpointer 4字节, instance data int 4字节(int基本数据类型存储数据为4个字节) 开启普通对象指针压缩 ,String 4字节,(String类型非基本数据类型,存储对象的指针,指针压缩后为4个字节) padding 4字节, 一共24字节。

2.synchronized的锁状态

在这里插入图片描述

2.1偏向锁

偏向锁定义

在同步代码块中,大部分情况下,并不会有多个线程并发执行,很大部分是单个线程反复请求执行。这种情况下直接使用重量级锁是很耗费资源的。

在这里插入图片描述偏向锁加锁过程

执行字节码指令mornitorEnter前。
1.向当前线程栈内,添加一条锁记录Lock Record ,锁记录的锁引用指向当前锁对象。(不需要CAS操作,栈内操作不涉及并发)
2.通过CAS设置锁对象的MarkWord,存储当前线程id。(锁对象可能存在并发操作,所以使用CAS)

偏向锁释放锁过程

偏向锁退出同步代码块字节码指令mornitorExit。
1.将当遍历前线程栈,并与当前锁对象相关的所有锁记录Lock record都拿到,需要修复锁记录和Markword,使其变成无锁状态。
2.再检查锁对象MarkWord,当前锁对象是偏向锁状态,就不用管,完成了。(锁对象保留了偏向线程的偏向锁,好处是,下此获取偏向锁时,对比一下线程ID,添加一条锁记录就ok了,不涉及一条CAS操作)

2.2轻量级锁

轻量级锁定义

轻量级锁是多线程交替执行,不存在并发执行现象。

MarkWord中轻量级锁状态:

轻量级锁加锁过程

(1)向当前线程栈内添加一条锁记录Lock record,锁记录的锁引用Owner指向当前锁对象地址。
(2)当前锁对象生成一个无锁状态的MarkWord值,即displaceMarkWord,并把displaceMarkWord的值保存在锁记录的displace字段中。
(3)使用CAS设置当前锁对象的MarkWord值为轻量级锁状态,保存指向栈中锁记录的指针。(无锁状态下,CAS一定会设置成功;但已经处于轻量级锁状态时,会这是失败,此状态有两种情况1.相同线程锁重入,锁记录中的displace字段置为null即可。2.不同线程 锁升级)。
(4)将锁对象的对象头的MarkWord替换为指向锁记录的指针.

线程栈内指向当前锁对象的所有锁记录,既是锁重入的次数。

轻量级锁释放锁过程

1.从当前线程栈中找到“最后一条”,指向当前锁对象的锁记录,把锁引用字段置为null(如果displace字段为null,则是重入锁记录)
2.通过CAS设置锁对象的MarkWord值,设置为锁记录中的displaceMarkWord值,为无锁状态,轻量级锁释放成功。(CAS失败,表示锁在膨胀中或者是重量级锁)

轻量级锁主要有两种

1.自旋锁
2.自适应自旋锁

在这里插入图片描述

2.3 重量级锁

为什么说重量级锁开销大呢?
主要是,当系统检查到锁是重量级锁之后,会把等待想要获得锁的线程进行阻塞,被阻塞的线程不会消耗cup。但是阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态转换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长。
这就是说为什么重量级线程开销很大的。

重量级锁定义

1.重量级锁是为了解决多线程并发执行情况下的锁。通过管程对象ObjectMornitor实现.

2.MarkWord中重量级锁状态:
在这里插入图片描述
3.管程模型ObjectMornitor对象,C++实现
在这里插入图片描述
4. 每一个 JAVA 对象都会与一个监视器 monitor 关联,我们可以把它理解成为一把锁,当一个线程想要执行一段被synchronized 修饰的同步方法或者代码块时,该线程得先获取到 synchronized 修饰的对象对应的 monitor。monitorenter 表示去获得一个对象监视器。monitorexit 表示释放 monitor 监视器的所有权,使得其他被阻塞的线程可以尝试去获得这个监视器monitor 依赖操作系统的 MutexLock(互斥锁)来实现的, 线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能。

重量级锁加锁过程

①检擦MarkWord中重量级锁状态和指向管程对象的指针。
②抢占锁的线程到管程通过CAS操作,自旋的方式将owner字段设置成当前线程(轻升级到重时,设置的是持有轻的线程),设置成功返回,不成功说明有线程抢用。
③如果通过CAS抢占锁失败,当前线程会变成block状态,进入同步队列entryList中,当访问 Object
的前驱(获得了锁的线程)释放了锁,则该释放操作唤醒阻塞在同步队列中的线程,使其重新尝试对监视器的获取。 等待再次竞争锁。
④当前线程调用wait方法时会封装成waiter节点,存入WaitSet队列中,等待被唤醒,notify,notifyAll。

重量级锁释放锁过程

1.将ObjectMorniter的字段设置为null
2.检查竞争队列和EntryList是否有等待线程,有的话唤醒去抢占锁。

附加例题

1.什么情况下会有锁膨胀操作?

  1. 线程调用轻量级锁或偏向锁的hashCode方法时,会膨胀为重量级锁。(因为偏向锁和轻量级锁状态下,MarkWord没办法存储hash值)
  2. 持锁线程调用wait()方法,会锁膨胀为重量级锁(因为偏向锁和轻量级锁,没有管程对象,不能存放线程节点)
  3. 在轻量级锁状态下,有并发竞争,锁膨胀为重量级锁。(此时会获取一个空管程对象,通过CAS设置MarkWord为膨胀状态,CAS设置管程owner字段为当前的线程。)

注:

  1. 当一个对象已经计算过identity hash code,它就无法进入偏向锁状态;

  2. 当一个对象当前正处于偏向锁状态,并且需要计算其identity hash code的话,则它的偏向锁会被撤销,并且锁会膨胀为重量锁;

  3. 重量锁的实现中,ObjectMonitor类里有字段可以记录非加锁状态下的mark word,其中可以存储identity hash code的值。或者简单说就是重量锁可以存下identity hash code。

  4. 这里讨论的hash code都只针对identity hash code。用户自定义的hashCode()方法所返回的值跟这里讨论的不是一回事。 Identity hash code是未被覆写的 java.lang.Object.hashCode() 或者 java.lang.System.identityHashCode(Object) 所返回的值。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

synchronized锁详解 的相关文章

  • ConcurrentHashMap、synchronized与线程安全

    最近做的项目中遇到一个问题 xff1a 明明用了ConcurrentHashMap xff0c 可是始终线程不安全 除去项目中的业务逻辑 xff0c 简化后的代码如下 xff1a public class Test40 public sta
  • 多线程(十)多线程编程示例

    文章目录 一 交替输出1A2B3C4D5E 1 1 synchronized wait notify 1 2 Condition await signal 二 生产者 消费者问题 2 1 synchronized wait notify 2
  • Java中synchronized同步锁用法及作用范围

    Java 中的 synchronized 关键字可以在多线程环境下用来作为线程安全的同步锁 本文主要对 synchronized 的作用 以及其有效范围进行讨论 Java中的对象锁和类锁 java的对象锁和类锁在锁的概念上基本上和内置锁是一
  • Lock 接口与 synchronized 关键字的区别

    拷贝别的博主总结的9点不同 1 JDK版本不同 synchronized关键字产生于JKD1 5之前 是低版本保证共享资源同步访问的主要技术 Lock接口产生于JDK1 5版本 位于著名的java util concurrent并发包中 是
  • Java多线程编程基础篇(二)-多线程同步关键字

    一 多线程同步关键字 synchronized 1 概念 synchronized保证方法或者代码块在运行时 同一时刻只有一个方法可以进入到临界区 同时它还可以保证共享变量的内存可见性 当多个并发线程访问同一个对象object中的同步代码块
  • 13张图,带大家深入理解Synchronized

    目录 前言 内容大纲 Synchronized使用方式 普通函数 静态函数 代码块 Synchronized原理 Synchronized优化 锁粗化 锁消除 锁升级 偏向锁 轻量级锁 重量级锁 前言 Java并发编程系列第二篇Synchr
  • 在 Java 临界区中,我应该同步什么?

    在 Java 中 在代码中声明临界区的惯用方法如下 private void doSomething thread safe code synchronized this thread unsafe code thread safe cod
  • 语句执行与同步方法执行交错

    我在理解同步关键字功能时遇到一些困难 根据java文档和其他教程 据说当使用synchronized关键字时 两个线程之间不可能在该方法的语句之间进行交错 但是 请看下面的代码 public class LockThread impleme
  • 为什么 Java 同步不能按预期工作?

    我试图弄清楚同步方法是如何工作的 根据我的理解 我创建了两个线程T1 and T2这将调用相同的方法addNew 由于该方法是同步的 难道它不应该先为一个线程执行 for 循环的所有迭代 然后再为另一个线程执行吗 输出不断变化 有时打印正确
  • 同步块是否会阻止其他线程向同步列表中插入数据?

    所以我正在开发一个多线程java应用程序 我有这个问题 List
  • Java 中使用同步块的并发未给出预期结果

    下面是一个简单的 java 程序 它有一个名为 cnt 的计数器 该计数器会递增 然后添加到名为 monitor 的列表中 cnt 由多个线程递增 值由多个线程添加到 monitor 在方法 go 的末尾 cnt 和 monitor siz
  • 通过此与虚拟对象同步

    到目前为止 我已经看到了同步块的用法 但最近我了解到使用虚拟对象更好 我发现以下与此相关的主题 Java同步方法锁定对象或方法 总而言之 在下面的代码中 两个不同的对象不能同时运行 addA 和 addB 因为它们都使用 this 作为锁
  • Java 线程和同步块

    假设我正在执行一个synchronized某个线程内和内的代码块synchronized我调用一个方法 该方法生成另一个线程来处理需要与第一个方法相同的锁的同步代码块 所以在伪 Java 代码中 public void someMethod
  • Java的happens-before和同步

    我对 Java 有一点不同意见发生在之前和同步 想象一下以下场景 主线程 MyObject o new MyObject 0 synchronized sharedMonitor 1 add the object to a shared c
  • 迭代同步集合

    我在这里问了一个关于迭代 a 的问题Vector 我已经得到了一些好的解决方案的答复 但我读到了另一种更简单的方法 我想知道这是否是一个好的解决方案 synchronized mapItems Iterator
  • 同步与 ReadWriteLock 性能

    我试图证明当有很多读者而只有一些作者时同步会更慢 不知怎的 我证明了相反的情况 以 RW 为例 执行时间为 313 ms package zad3readWriteLockPerformance import java util Array
  • Java 同步引用

    我有A级和B级 public class A private static List
  • 异步映射中的同步部分

    我有一个大的 IO 函数 它将持续从文件夹加载数据 对数据执行纯计算 然后写回 我正在多个文件夹上并行运行此函数 mapConcurrently iofun folderList from http hackage haskell org
  • Java并发中的AbstractQueuedSynchronizer

    What is AbstractQueuedSynchronizer在Java中concurrent locks包用来做什么 有人可以阐明它的方法吗doAcquireInterruptibly and parkAndCheckInterru
  • 分布式张量流 tf.train.SyncReplicasOptimizer 似乎不同步

    我使用两个工作程序 副本和一个参数服务器 喜欢 ps hosts hosta com 2222 worker hosts hosta com 2223 hostb com 2223 使用tf train SyncReplicasOptimi

随机推荐

  • JAVA基础知识(二)

    目录 1 循环遍历2 排序算法2 1 冒泡排序2 2 选择排序2 3插入排序 3 数组的查找3 1顺序查找3 2二分查找 xff08 折半查找 xff09 4 Arrays工具类5 可变参数 1 循环遍历 JAVA中的常用的循环遍历有for
  • JAVA基础知识(三)

    目录 1 JAVA关键字1 1关键字 xff1a private1 2关键字 xff1a this1 3关键字 xff1a static1 4关键字 xff1a super1 5关键字 xff1a final 2 继承继承中的构造方法 3
  • JAVA基础知识(四)

    目录 1 抽象类 抽象方法2 接口interface3 多态4 对象转型5 内存分析6 设计原则7 单例设计模式 1 抽象类 抽象方法 1 抽象方法和抽象类必须使用abstract修饰符修饰 xff0c 有抽象方法的类只能被定义成抽象类 x
  • 基于mybatis-plus的代码自动生成工具(自定义模板)

    MyBatis Plus是一个MyBatis框架的增强工具 xff0c 在MyBatis的基础上只做增强不做改变 xff0c 为简化开发 提高效率而生 对于mybatis plus不了解的同学 xff0c 可以去MyBatis Plus官网
  • capabilities: ambient capabilities说明

    linux capability介绍 最早之前 xff0c linux对任务权限分为privileged processes xff08 UID等于0 xff0c 属于超级用户或者root用户 xff09 和unprivileged pro
  • 生死簿后台管理系统(有趣、放松下大脑)

    第一幕 xff1a 缘起 听说阎王爷要做个生死簿后台管理系统 xff0c 我们派去了一个程序员 996程序员做的梦 xff1a 第一场 xff1a 团队招募 为了应对地府管理危机 xff0c 阎王打算找 人 开发一套地府后台管理系统 xff
  • 年度最受欢迎的Python的书籍,还不来看看!

    Python是一种通用的解释型编程 xff0c 主要用于Web开发 机器学习和复杂数据分析 Python对初学者来说是一种完美的语言 xff0c 因为它易于学习和理解 xff0c 随着这种语言的普及 xff0c Python程序员的机会也越
  • 线程安全的实现方法

    1 互斥同步 互斥同步是一种常见也是最主要的并发正确性保障手段 xff0c 同步是指在多个线程并发访问共享数据时 xff0c 保证共享数据在同一个时刻只被线程使用 互斥实现方式有互斥量 xff0c 临界区 xff0c 信号量等手段 JAVA
  • CATIA卸载不完全,导致右击新建中有残留——注册表处理方法

    我通过计算机自带的 卸载程序 xff0c 卸载CATIA时 xff0c 出现 找不到指定文件 的情况 xff0c 又不想下载相关第三方软件 xff0c 遂尝试本地删除 首先删除安装目录下的文件 其次删除以下目录中的 Dassault Sys
  • BlackArch Linux安装VMware Tools教程

    BlackArch Linux安装VMware Tools教程 其实 xff0c 只要是Linux系统 xff0c 安装VMware Tools都是大同小异 xff0c 我曾经也给大家分享过一篇文章 xff1a VMware虚拟机 Linu
  • 二叉树的创建(C语言)

    树的基本性质和概念 1 p 61 q 43 1 其中p为顶点 xff0c q为边 2 结点的度 xff1a 子树的个数 xff0c 树的度 xff1a 结点度的最大值 3 连通且无圈 由于所有的树都可以转化为二叉树 xff0c 下列出二叉树
  • colmap多相机重建多场景及数据库数据快速修改方法

    1 colmap流程 1 1 新建项目 首先打开colmap xff0c 然后创建新的project xff0c 其中数据库目录和名称自己选定 xff0c 注意不要将它放到图像目录下即可 然后images选择的是图像目录 xff08 比如我
  • 安装EXSi服务器

    安装EXSi服务器 一 创建VMware EXSi5 1服务器 1 在VMware服务器中创建新的虚拟机 xff0c 选择自定义配置 2 硬件兼容性选择Workstation11 0版本 xff08 最高版本 xff09 3 选择 稍后安装
  • 2020 关于mybatis的resultMap=“BaseResultMap“说明

    关于mybatis的resultMap 61 34 BaseResultMap 34 说明 mybatis 非常的智能 xff0c 如果配置了resultMap xff0c 返回值统一使用 resultMap 61 BaseResultMa
  • 高斯数库安装笔记

    手机用鸿蒙 服务用欧拉 欧拉里有高斯数据库 欧拉 openEuler 22 03 LTS x86 64 dvd iso 里预装gauss数据库 语言选 english 在安装系统的时候在 一个界面把软件 gt 服务 gt opengauss
  • bclinux使用yum卡主解决方法、bclinux用rpm安装本地源方法、Failed to set locale, defaulting to C.UTF-8解决方法、bclinux镜像下载地址

    文章目录 环境说明bclinux使用yum卡主解决方法说明解决方法方法1方法2 bclinux用rpm安装离线源下载http包安装http包测试 Failed to set locale defaulting to C UTF 8说明解决方
  • 多进程-生产者,消费者问题

    进程同步经典问题 生产者消费者 生产者消费者模型描述 xff1a 生产者 消费者问题可以描述为 xff1a 两个或者更多的进程 xff08 线程 xff09 共享同一个缓冲区 xff0c 其中一个或多个进程 xff08 线程 xff09 作
  • VUE专题 elementUI中tableColumn 的formatter用法

    示例一 页面中 对应方法 示例二 对应方法 这里是为了截取前面的时间戳
  • 电脑安装windows和Ubuntu双系统后,不进入选择系统界面

    怎么安装双系统就不用交了吧 xff0c 在网上下一个Rufus xff0c 再去ubuntu官网下载所需版本景象 xff0c 就可以制作ubuntu系统盘 xff0c 在windows中压缩新建卷 xff0c 创建新分区 xff0c 重启
  • synchronized锁详解

    1 对象内存布局 对象头 MarkWord 对象头 xff0c 8字节 包括了对象的hashCode 对象的分代年龄 锁标志位等 结构如下图所示 xff1a 类指针classPointer 对象指向它的类元素的指针 在不开启对象指针压缩的情