JUC并发编程 06——Synchronized与锁升级

一.Java对象内存布局和对象头

在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头(Header) 、实例数据 (Instance Data) 和 对文填充 (Padding)。

  • 对象内部结构分为:对象头、实例数据、对齐填充(保证8个字节的倍数)。
  • 对象头分为对象标记(markOop)和类元信息(klassOop),类元信息存储的是指向该对象类元数据(klass)的首地址。

对象头

在64位系统中,Mark Word占了8个字节,类型指针占了4个字节(采用了压缩指针,压缩前是8个)。

对象标记Mark Word

类元信息(又叫类型指针)

指向它的类元数据(方法区的Klass对象)的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

实例数据

存放类的属性(Field)数据信息,包括父类的属性信息。

对齐填充

虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐
这部分内存按8字节补充对齐。

二.Synchronized与锁升级

2.1为什么 JDK1.6 以前synchronized是重量级锁

什么是上下文操作

线程多,不断阻塞、唤醒,一次阻塞+唤醒操作需要两次上下文切换:

  1. 就是第一次由用户态切换到内核态,由内核将线程阻塞掉,将线程数据原本在寄存器或者cache运送到内存或队列中
  2. 第二次由用户态切换到内核态,由内核将线程唤醒,将线程数据从内存或者队列送到寄存器或cache中。如果频繁进行上下文切换,CPU就大多数消耗在内核数据共享中,影响了真正工作线程功能

上下文切换过高会导致CPU像个搬运工,频繁在寄存器和运行队列之间奔波 ,更多的时间花在了线程切换,而不是真正工作的线程上。直接的消耗包括CPU寄存器需要保存和加载,系统调度器的代码需要执行。间接消耗在于多核cache之间的共享数据。

因为监视器锁(monitor)依赖底层操作系统的原语 pthread_mutex_lock 实现,当多个线程同时调用这个函数的时候,会让每一个线程都切换到内核态,由内核协调哪个线程获取到锁,哪些线程无法获取到锁。获取锁成功的线程会被唤醒,获取锁失败的线程会被内核进行阻塞。所以它是一个重量级操作操作系统实现线程之间的切换需要从用户态到内核态的转换,这种状态切换需要耗费处理器时间。

总之一句话,重量级锁是需要依靠操作系统来实现互斥锁的,这导致大量上下文切换,消耗大量CPU,影响性能。

Java SE 1.6开始,为了减少获得锁和释放锁所带来的性能消耗,引入了轻量级锁偏向锁。在Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级为偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

2.2偏向锁

Hotspot 的作者经过研究发现,大多数情况下:锁不仅不存在多线程竞争,还存在锁由同一线程多次获得的情况,为了让线程获得锁的代价更低而引入了偏向锁,这个线程就是锁的偏向线程,它的出现是为了解决只有在一个线程执行同步时提高性能。

  1. 首先获取锁对象的 Markword,判断是否处于可偏向状态。(biased_lock=1、且 ThreadId 为空)
  2. 如果是可偏向状态,则通过 CAS 操作,把当前线程的 ID 写入到 MarkWord
    1. 如果 cas 成功,那么 markword 就会变成这样。 表示已经获得了锁对象的偏向锁,接着执行同步代码块
    2. 如果 cas 失败,说明有其他线程已经获得了偏向锁,这种情况说明当前锁存在竞争,需要撤销已获得偏向锁的线程,并且把它持有的锁升级为轻量级锁(这个操作需要等到全局安全点,也就是没有线程在执行字节码)才能执行
  3. 如果是已偏向状态,需要检查 markword 中存储的 ThreadID 是否等于当前线程的 ThreadID
    1. 如果相等,不需要再次获得锁,可直接执行同步代码块
    2. 如果不相等,说明当前锁偏向于其他线程,需要撤销偏向锁并升级到轻量级锁

偏向锁撤销

偏向锁使用一种等到竞争出现才释放锁的机制,只有当其他线程竞争锁时,持有偏向锁的原来线程才会被撤销。撤销需要等待全局安全点(该时间点上没有字节码正在执行),同时检查持有偏向锁的线程是否还在执行: 

  1. 第一个线程正在执行synchronized方法(处于同步块),它还没有执行完,其它线程来抢夺,该偏向锁会被取消掉并出现锁升级此时轻量级锁由原持有偏向锁的线程持有,继续执行其同步代码,而正在竞争的线程会进入自旋等待获得该轻量级锁。
  2. 第一个线程执行完成synchronized方法(退出同步块),则将对象头设置成无锁状态并撤销偏向锁,重新偏向 。

关闭偏向锁

偏向锁在 Java 6 和 Java 7 里是默认启用的,但是它在应用程序启动几秒钟之后才激活,如有必要可以使用JVM参数来关闭延迟:-XX:BiasedLockingStartupDelay=0

如果你确定应用程序里所有的锁通常情况下处于竞争状态,那么偏向锁就是多余的,此时使用偏向锁是得不到提升的,反而可能降低系统性能,可以通过JVM参数关闭偏向锁:-XX:-UseBiasedLocking

从 JDK 15 开始,偏向锁被官方标记为废弃状态

2.3轻量级锁

轻量级锁加锁

加锁的过程很奇妙:线程在执行同步代码块之前,JVM会先在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头中的Mark Work 复制到锁记录中。接下来线程会尝试使用CAS操作将对象头中的Mark Word替换为指向锁记录的指针。如果成功则获取锁,失败则表示被占用,自旋重试。

轻量级锁解锁

轻量级锁解锁时,会使用原子性的CAS操作将栈帧中的 Mark Word替换回到对象头,如果成功则表示没有竞争。失败则表示存在竞争,锁膨胀为重量级锁。

经过上面的学习,我们知道了JVM底层几种锁的实现,下面总结了几种锁的优缺点

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

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

相关文章

软件设计师——计算机组成原理(三)

📑前言 本文主要是【计算机组成原理】——软件设计师——计算机组成原理的文章,如果有什么需要改进的地方还请大佬指出⛺️ 🎬作者简介:大家好,我是听风与他🥇 ☁️博客首页:CSDN主页听风与他 …

runCatching异常捕获onSuccess/onFailure返回函数,Kotlin

runCatching异常捕获onSuccess/onFailure返回函数,Kotlin fun test(a: Int, b: Int) {runCatching {a / b}.onSuccess {println("onSuccess: $it")return ok(it)}.onFailure {println("onFailure: $it")return fail(it)} }fun ok(o: Any) {prin…

echarts地图的常见用法:基本使用、区域颜色分级、水波动画、区域轮播、给地图添加背景图片和图标、3d地图、飞线图

前言 最近几天用echarts做中国地图,就把以前写的demo:在vue中实现中国地图 拿来用,结果到项目里直接报错了,后来发现是因为版本的问题,没办法只能从头进行踩坑了。以下内容基于vue3 和 echarts 5.32 基本使用 获取地…

Linux环境下HTTP请求的代码详解与实例

嘿,Linux狂热者们,是时候让我们在这个充满激情的平台上探索一下HTTP协议的奥秘了。我知道,对于这个我们每天都在使用,但却可能没深入了解过的HTTP,你们一定充满了好奇和期待。那么,让我们一起踏上这趟探索之…

教师如何维护学生的自尊心

作为教师,我们不仅要传授知识,更要关心学生的身心健康,特别是他们的自尊心。自尊心是个人自我价值的重要体现,对学生的学习、生活和未来的发展都有深远的影响。因此,维护学生的自尊心是教师的重要责任。 教师要尊重每…

波奇学Linux:进程终止

写时拷贝底层原理图 子进程谁先运行,由调度器决定 进程退出场景 代码运行完毕,结果正确:有返回值,返回0 代码运行完毕,结果不正确:有返回值,返回非0 代码异常终止。没有返回值 return 0的…

十七、如何将MapReduce程序提交到YARN运行

1、启动某个节点的某一个用户 hadoopnode1:~$ jps 13025 Jps hadoopnode1:~$ yarn --daemon start resourcemanager hadoopnode1:~$ jps 13170 ResourceManager 13253 Jps hadoopnode1:~$ yarn --daemon start nodemanager hadoopnode1:~$ jps 13170 ResourceManager 15062 Jp…

【小沐学Python】Python实现通信协议(grpc)

文章目录 1、简介2、安装3、定义接口4、编译生成5、代码编写5.1 服务端5.2 客户端5.3 运行测试 结语 1、简介 https://grpc.io/docs/what-is-grpc/introduction/ https://github.com/grpc/grpc gRPC 是一种现代、开源、高性能的远程过程调用 (RPC) 可以…

Python生成器(Generator)的应用场景和使用(继续更新...)

学习网页: Welcome to Python.orghttps://www.python.org/https://www.python.org/ Python生成器(Generator) 生成器在Python中有很多应用场景,以下是一些常见的应用场景: 处理大数据量和耗时操作的场景&#xff…

【STM32】STM32学习笔记-对射式红外传感器计次 旋转编码器计次(12)

00. 目录 文章目录 00. 目录01. NVIC相关函数1.1 NVIC_PriorityGroupConfig函数1.2 NVIC_PriorityGroup类型1.3 NVIC_Init函数1.4 NVIC_InitTypeDef类型 02. 外部中断相关API2.1 GPIO_EXTILineConfig2.2 EXTI_Init2.3 EXTI_GetITStatus2.4 EXTI_ClearITPendingBit2.5 中断回调函…

大数据技术之 Kettle(PDI)

Kettle 第一章 Kettle概述1.1、ETL简介1.2、Kettle简介1.3、作业 和 转换 概念1.4、核心组件1.5、下载安装 第二章 控件使用2.1、初体验:csv 转换 excel 示例2.2、转换2.2.1、输入控件2.2.1.1、表输入 2.2.2、输出控件2.2.2.1、表输出2.2.2.2、更新&插入/更新2.…

【数据结构—队列的实现】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一、队列 1.1队列的概念及结构 二、队列的实现 2.1头文件的实现—Queue.h 2.2源文件的实现—Queue.c 2.3源文件的测试—test.c 三、测试队列实际数据的展示 3.…

Python语言学习笔记之八(文件IO)

本课程对于有其它语言基础的开发人员可以参考和学习,同时也是记录下来,为个人学习使用,文档中有此不当之处,请谅解。 1、什么是文件I/O 在Python中,文件IO(输人/输出)是指与文件进行交互的过程。这包括读…

MybatisPlus进阶,UUID VS SnowFlake(雪花算法)

目录 一、什么是MybatisPlus 为什么要学MybatisPlus? 特性: 二、快速入门 2.1快速初始化一个空的spring boot 项目 2.2配置依赖 2.3配置(连接数据库) 2.4在spring boot启动类中添加MapperScan注解,扫描Mapper文件夹: 2.5…

如何在安装了巨魔2的iphone中运行Theos编译的本地化二进制工具:Bootstrap

如何在安装了巨魔2的iphone中运行Theos编译的本地化二进制工具:Bootstrap 一、首先从https://github.com/34306/iPA/releases/tag/bstr下载jb.zip、jb_with_jb_folder.zip、prefs_fix.ipa三个文件。 二、然后使用Filza文件管理器把jb.zip解压后复制到/var/containers/jb目录&…

【深度学习目标检测】四、基于深度学习的抽烟识别(python,yolov8)

YOLOv8是一种物体检测算法,是YOLO系列算法的最新版本。 YOLO(You Only Look Once)是一种实时物体检测算法,其优势在于快速且准确的检测结果。YOLOv8在之前的版本基础上进行了一系列改进和优化,提高了检测速度和准确性。…

设计模式 五种不同的单例模式 懒汉式 饿汉式 枚举单例 容器化单例(Spring单例源码分析) 线程单例

单例模式 第一种 饿汉式 优点:执行效率高,性能高,没有任何的锁 缺点:某些情况下,可能会造成内存浪费 /*** author LionLi*/ public class HungrySingleton {private static final HungrySingleton hungrySingleton n…

英文论文降重修改技巧 papergpt

大家好,今天来聊聊英文论文降重修改技巧,希望能给大家提供一点参考。 以下是针对论文重复率高的情况,提供一些修改建议和技巧,可以借助此类工具: 英文论文降重修改技巧 作为网站编辑,我们经常需要处理大量…

冒泡排序法

1.数组排序 题目描述 对数组的元素按从小到大进行排序。输入有两行 第一行有一个整数n( 5 < n < 10 ) 第二行有n个整数输出输出更新后的数组 样例 输入复制 8 1 2 3 6 8 7 4 5 输出复制 1 2 3 4 5 6 7 8 #include<iostream> using namespace std; int main(…

遥感图像之多模态检索AMFMN(支持关键词、句子对图像的检索)论文阅读、环境搭建、模型测试、模型训练

一、论文阅读 1、摘要背景 遥感跨模态文本图像检索以其灵活的输入和高效的查询等优点受到了广泛的关注。然而&#xff0c;传统的方法忽略了遥感图像多尺度和目标冗余的特点&#xff0c;导致检索精度下降。为了解决遥感多模态检索任务中的多尺度稀缺性和目标冗余问题&#xff…