【JUC进阶】详解synchronized锁升级

news/2024/5/22 6:42:35/文章来源:https://blog.csdn.net/weixin_51146329/article/details/129855577

文章目录

  • 1. synchronized概述
  • 2. synchronized 的实现原理
    • 2.1 Java对象组成
    • 2.2 Monitor
    • 2.3 从字节码角度看synchronized
  • 3. 锁升级
    • 3.1 偏向锁
    • 3.2 轻量级锁

1. synchronized概述

synchronized是一个悲观锁,可以实现线程同步,在多线程的环境下,需在操作同步资源的时候先加锁,避免共享资源出现问题。

因为加锁可以使得一个线程在一个时间点内只有一个线程可以访问,这样增加了安全性。

但是这样却损失了程序的执行性能,因为在加锁、抢夺锁、释放锁需要从用户态切换成内核态,属于操作系统层面的,因此比较消耗性能。

于是,在JDK6之后便引入了“偏向锁”和“轻量级锁”,共有4种锁状态,级别由低到高依次为:无锁状态偏向锁状态轻量级锁状态重量级锁状态。这几个状态会随着竞争情况逐渐升级。

锁状态说明及升级图示

synchronized 可以用在实例方法、静态方法、代码块上

  1. 修饰实例方法,对当前实例对象this加锁
  2. 修饰静态方法,对当前类的Class对象加锁
  3. 修饰代码块,指定加锁对象,对给定对象加锁

2. synchronized 的实现原理

想要了解synchronized 的实现原理,就要先知道Java对象是怎么存放的。因为synchronized不论是修饰方法还是代码块,都是通过持有修饰对象的锁来实现同步。


2.1 Java对象组成

Java对象分为三部分:

  1. 对象头,包括**Mark Word (标记字段)** 和 Klass Pointer(类型指针)
    • Mark Word用来存储对象自身的运行时数据
    • Klass Point 是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例
  2. 实例变量,存放类的属性数据信息,包括父类的属性信息,这部分内存按4字节对齐
  3. 填充字节,由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐

image-20230325143946906

Java对象有这三部分,锁就在对象的对象头Mark WordMark Word的结构如下,在64位虚拟机下,MarkWord是64bit大小的,其存储结构如下所示

img

img


2.2 Monitor

Monitor可以理解为是一个同步工具或者同步器,通常被描述为一个对象。每一个 Java 对象就有一把看不见的锁,称为内部锁或者 Monitor 锁。

Monitor 是线程私有的数据结构,每一个线程都有一个可用 monitor record 列表,同时还有一个全局的可用列表。每一个被锁住的对象都会和一个 monitor 关联,同时 monitor 中有一个 Owner 字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。

ObjectMonitor() {_count        = 0; //记录数_recursions   = 0; //锁的重入次数_owner        = NULL; //指向持有ObjectMonitor对象的线程 _WaitSet      = NULL; //调用wait后,线程会被加入到_WaitSet_EntryList    = NULL ; //等待获取锁的线程,会被加入到该列表
}

对于一个被synchronized 修饰的方法和代码块来说

  1. 当多个线程同时访问一个方法时,这些线程会被放入EntryList队列中,此时这些线程处于阻塞(Blocked)状态。
  2. 当一个线程获取到了对象的Monitor后,就进入可运行(running)状态,执行方法,此时ObjectMonitor对象的_owner就会指向当前线程,表示当前线程获取到了锁。并且锁的计数器_count需要加一。
  3. 当running状态的线程调用wait()方法,那么当前线程释放monitor对象,进入waiting状态,ObjectMonitor对象的owner变为null,count减1,同时线程进入WaitSet队列,直到有线程调用notify()方法唤醒该线程,则该线程进入EntryList队列,竞争到锁再进入_Owner区
  4. 当线程释放锁的时候,线程会释放Monitor对象,锁的计数器_count需要减一,当锁的计数器为0的时候,就会彻底释放锁。

Monitor对象存在于每个Java对象的对象头中,synchronized锁便是通过这种方式获取锁的。


2.3 从字节码角度看synchronized

这里有一个加锁的代码

public class Test {public int count = 0;public void addOne() {synchronized (this) {count++;}}
}

将这个Java程序编译成字节码class文件

 public void addOne();descriptor: ()Vflags: ACC_PUBLICCode:stack=3, locals=3, args_size=10: aload_01: dup2: astore_13: monitorenter // 进入同步方法4: aload_05: dup6: getfield      #2                  // Field count:I9: iconst_110: iadd11: putfield      #2                  // Field count:I14: aload_115: monitorexit // 退出同步方法16: goto          2419: astore_220: aload_121: monitorexit // 退出同步方法22: aload_223: athrow24: returnException table:

可见,字节码底层是通过monitorenter进入同步代码块的,通过monitorexit指令退出同步代码块的。

monitorexit指令有两个,第一个是正常退出同步代码块的情况。第二个则是由于同步代码块出现异常而出现释放锁的情况,这种设计可以有效避免死锁。


3. 锁升级

为什么会出现锁升级呢?

一开始,synchronized 无论是大并发还是小并发都属于重量级锁,效率低下,因为操作系统实现线程之间的切换时需要从用户态转换到核心态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高。

于是,在JDK6之后,在JVM层面对synchronized 进行了优化,为了减少锁的获取和释放所带来的性能消耗,引入了“偏向锁”和“轻量级锁”。也就出现了锁升级的情况。

注意,锁只可以升级但不能降级,但是偏向锁状态可以重置为无锁状态。

img


3.1 偏向锁

偏向锁的出现,是为了应对同一个线程多次获取一个锁的情况的出现,因此没有必要每次都要竞争锁,从而降低获取锁的代价。

偏向锁的核心思想是:如果一个线程获取到了锁,那么就进入偏向模式,此时Mark Word结构也变为偏向锁模式,当这个线程再次来请求获取锁,则无需在任何同步操作,直接获取锁。

加锁的时候,如果该锁对象支持偏向锁,那么Java虚拟机会通过CAS操作,将当前线程的地址也就是线程ID记录到对象头的标记字段,并且将标记字段的最后三位设置为101。

在这里插入图片描述

如果前面通过CAS加锁、解锁的时候,对比当前线程ID和Java对象头的线程ID,如果一直,就可以直接获取锁。

如果不一致,说明存在其他线程需要竞争锁对象,那么就需要查看Java对象头的记录的线程是否存活。

如果没有存活则会将锁对象重置为无锁状态,其他线程都可以竞争将其设置为偏向锁。

如果存活,那么立刻查找该线程的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。

img


3.2 轻量级锁

轻量级锁考虑的是竞争对象的线程不多,而且线程持有锁的时间也不长的情景。

轻量级锁的获取主要有两个情况

  1. 当偏向锁关闭的时候
  2. 由于多个线程竞争导致偏向锁升级为轻量级锁

线程A在获取轻量级锁的时候,会先把锁对象的Mark Word复制一份给线程A的栈帧中创建的用于存储锁记录的空间(Displaced Mark Word),然后使用CAS把对象头的内存替换成线程A存储的锁记录。如果成功,当前线程获得锁,如果失败,表示Mark Word已经被替换成了其他线程的锁记录,说明在与其它线程竞争锁,当前线程就尝试使用自旋来获取锁。

如果自旋次数到了线程B还没有释放锁,或者线程B还在执行,线程A还在自旋等待,这时又有一个线程C过来竞争这个锁对象,那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞,防止CPU空转。

轻量级锁的释放
在释放锁时,当前线程会使用CAS操作将·Displaced Mark Word内容复制回锁的MarkWord里面。如果没有发生竞争,那么这个复制的操作会成功。如果有其他线程因为自旋多次导致轻量级锁升级成了重量级锁,那么CAS操作会失败,此时会释放锁并唤醒被阻塞的线程。

img


参考:

  1. 【JUC】10. synchronized与锁升级_synchronized性能下降_起名方面没有灵感的博客-CSDN博客
  2. 详解Synchronized底层实现,锁升级的具体过程,与Lock的区别 - 掘金 (juejin.cn)
  3. synchronized四种锁状态的升级 - 掘金 (juejin.cn)
  4. 大彻大悟synchronized原理,锁的升级 - 掘金 (juejin.cn)

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

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

相关文章

DIN35电压电流转频率单位脉冲输出信号变换器集电极开路隔离变送器

主要特性 将直流电压或电流信号转换成单位脉冲信号。 精度等级:0.1 级、0.2 级。产品出厂前已检验校正,用户可以直接使用。 国际标准信号输入:0-5V/0-10V/1-5V 等电压信号,0-10mA/0-20mA/4-20mA 等电流信号。 输出标准信号:0-5KHz/0-…

Flink CDC 在京东的探索与实践

摘要:本文整理自京东资深技术专家韩飞,在 Flink Forward Asia 2022 数据集成专场的分享。本篇内容主要分为四个部分: 京东自研 CDC 介绍京东场景的 Flink CDC 优化业务案例未来规划点击查看直播回放和演讲 PPT 一、京东自研 CDC 介绍 京东自研…

小白学Pytorch系列- -torch.distributions API Distributions (1)

小白学Pytorch系列- -torch.distributions API Distributions (1) 分布包包含可参数化的概率分布和抽样函数。这允许构造用于优化的随机计算图和随机梯度估计器。这个包通常遵循TensorFlow分发包的设计。 不可能通过随机样本直接反向传播。但是,有两种主要方法可以…

tomcat中出现RFC7230和RFC3986问题解析

问题截图 问题分析 出现上述问题,是因为各版本tomcat中对特殊字符和请求路径中携带中文参数而产生的错误提示。 解决办法 1、调整tomcat版本 tomcat 7.0.76之前的版本不会出现类似问题 2、tomcat9之前,修改tomcat目录底下的/conf/catalina.properti…

chapter-5 数据库设计

以下课程来源于MOOC学习—原课程请见:数据库原理与应用 考研复习 引言 设计的时候: 我们为什么不能设计成R(学号,课程号,姓名,所咋系,系主任,成绩)? 因为存在数据冗余…

C++算法初级7——二分查找

C算法初级7——二分查找 文章目录C算法初级7——二分查找在升序的数组上进行二分查找总结应用范围应用二分查找的原理:每次排除掉一半答案,使可能的答案区间快速缩小。 二分查找的时间复杂度:O(log n),因为每次询问会使可行区间的…

appium+python自动化测试启动app

一、部署环境 1、依次下载安装以下工具,并配置环境变量: android sdk Nodejs appium appium-doctor Appium-Python-Client pycharm64 ps:安装包下载和配置环境变量的操作步骤跟着网上各路大神的帖子一步一步做就好了,没啥难度 二、连…

Machine Learning-Ex4(吴恩达课后习题)Neural Networks Learning

目录 1. Neural Networks 1.1 Visualizing the data 1.2 Model representation 1.3 Feedforward and cost function 1.4 Regularized cost function 2. Backpropagation 2.1 Sigmoid gradient 2.2 Random initialization 2.3 Backpropagation 2.4 Gradient Checking…

【数据库原理 • 四】数据库设计和规范化理论

前言 数据库技术是计算机科学技术中发展最快,应用最广的技术之一,它是专门研究如何科学的组织和存储数据,如何高效地获取和处理数据的技术。它已成为各行各业存储数据、管理信息、共享资源和决策支持的最先进,最常用的技术。 当前…

linux之jdk1.8环境安装与配置和Maven安装与配置

文章目录一、jdk1.8环境安装1、官网下载&#xff1a;<https://www.oracle.com/java/technologies/downloads/#java8>2、在usr文件夹下新建一个java文件夹3、解压完成后&#xff0c;将文件jdk文件传入到java目录下二、配置环境&#xff08;重点&#xff09;1、按 i 进行编…

蓝牙技术|苹果获空间音频新专利,AirPods可动态调整声学输出

美国商标和专利局&#xff08;USPTO&#xff09;公示的清单显示&#xff0c;苹果在近日获得了一项名为“测定虚拟聆听环境”的新专利。据悉&#xff0c;该技术可以改善用户的聆听体验&#xff0c;增强空间音频的沉浸感&#xff0c;未来有望应用在AirPods上。 这项专利技术可以…

代码随想录_二叉树_二叉树的层序遍历十道题

leetcode102.二叉树的程序遍历 102. 二叉树的层序遍历 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&…

文心一言对于宣传文案理解

前言 前段时间对于文心一言开放部分内测邀请&#xff0c;有幸获得邀请内测权限&#xff01;抱着试一试的态度对其进行了使用&#xff0c;结果还是比较满意的。我们来看一下我所说的满意是否能够达到你的要求&#xff01;&#xff01;&#xff01; 使用逻辑 文心一言的使用还…

FPGA lattice 深力科LCMXO3LF-4300C-6BG256I 可实现高效、灵活和安全的工业应用开发 低功耗FPGA解决方案详情讲解

FPGA lattice 深力科LCMXO3LF-4300C-6BG256I 可实现高效、灵活和安全的工业应用开发 低功耗FPGA解决方案详情讲解 超低密度FPGA 是最新的立即启用、非挥发性、小型覆盖区 FPGA&#xff0c;采用先进的封装技术&#xff0c;能让每个元件达到最低成本。此系列采用最新的小型封装&…

android12 displayArea学习

一&#xff1a;数据结构分析 1&#xff1a;android 12 WindowContainer 的类继承关系如下 下图为 WindowContainer 简要的对象图。 下图是 Aosp默认的display层次结构对象图。 Aosp定义的feature有如下 FEATURE_ROOT 0; FEATURE_DEFAULT_TASK_CONTAINER 1; FEATURE_WINDOW_…

正版软件 Directory Opus 12 Pro Windows 平台上的资源管理器,定是功能完全、可定制化程度高的那款。

Directory Opus 是一款 Windows 平台上的资源管理器&#xff0c;定是功能最完全、可定制化程度最高的那款。你可以通过它完成几乎所有操作&#xff0c;包括查看图片元信息、预览图片、阅读文本文件内容、批量重命名、操作压缩文件以及 FTP 同步请求等。 Directory Opus 是一款由…

使用大华惠智双目半球网络摄像机DH-IPC-HD4140X-E2获取人流量统计数据

记录一下使用Java的SpringBoot大华SDK在智慧公厕项目中使大华惠智双目半球网络摄像机DH-IPC-HD4140X-E2获取人流量统计数据 首先根据说明书登录摄像头&#xff0c;一般摄像头都有自己的账号和密码(可能是admin admin 也可能是admin 888888 还有可能是admin 12345)&#xff0c;…

DriveGPT、车企订单背后,为什么毫末每年都能搞出新东西?

作者 | 祥威 编辑 | 德新 4月11日&#xff0c;毫末智行正式发布自动驾驶生成式大模型 DriveGPT&#xff0c;中文名 雪湖海若&#xff0c;可以提升自动驾驶认知能力&#xff0c;最终提升规控效率。 雪湖海若的核心&#xff0c;是将各种驾驶场景作为Token输入到模型中&#xff…

零基础搭建私人影音媒体平台【远程访问Jellyfin播放器】

文章目录1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置4.公网访问测试5. 结语1. 前言 随着移动智能设备的普及&#xff0c;各种各样的使用需求也被开发出来&#xf…