Synchronized的实现和锁升级

1.JVM是如何处理和识别Synchronized的?

我们从字节码角度分析synchronized的实现:

  1. Synchronized(锁对象){}同步代码块底层实现方式是monitorentermonitorexit指令。

  2. 修饰普通同步方法时底层实现方式是执行指令会检查方法是否设置ACC_SYNCHRONIZED,如果设置了,则会先持有monitor锁(其实就是管程,锁对象),然后在执行方法,最后释放锁(无论方法执行完或出现异常)。

  3. 修饰静态同步方法时底层实现方式是执行指令会检查方法是否同时设置ACC_STATIC和ACC_SYNCHRONIZED,ACC_STATIC也用于分辨锁是类锁还是对象锁

2.为什么任何一个类的对象都可以成为锁对象?

        在HotSpot虚拟机中,监视器monitor采用的是ObjectMonitor实现的,在Java中,Object是每个类的父类,所以每个对象天生都带着一个对象监视器。在ObjectMonitor.java源代码中我们发现里面调用了objectMonitor.cpp文件,在objectMonitor.cpp里面又调用了ObjectMonitor.hpp,而在hpp文件中很明确的记录了正在持有此锁的线程、锁的重入次数等数据。

 

3.Synchronized锁的升级

Synchronized锁的状态主要依赖对象头中的MarkWord中锁标志位偏向锁位

3.1下面我们就表述锁升级的过程(重点)

        初始状态下,一个对象被实例化后,如果还没有任何线程使用此锁,那么它就为无锁状态(偏向锁位为0,锁标志位为01),当线程A第一次占用此锁时,MarkWord中会记录线程A的线程id,然后升级为偏向锁(偏向锁位为1,锁标志位为01),然后下一个线程访问时,会看MarkWord中记录的线程id是否和访问的线程一致,如果一致,就相当于还是线程A一直在访问,那么就会自动的获取锁,无需每次CAS去更新对象头,但是如果发现线程的id不一致,那么就发生了竞争,比如线程B来访问了,发现MarkWord中记录的线程id和自己的不一致,那么就会尝试使用CAS来替换MarkWord里面的线程id为自己线程B的线程id,如果修改竞争成功了,那么ok,MarkWord里面的线程idA更换为线程B的id,锁不会升级,还是偏向锁,但是如果线程B修改竞争失败,那么锁的状态就需要发生改变了,首先就是要先撤销偏向锁,先等待全局的安全点(STW),同时检查正持有偏向锁的线程A执行到哪里了,如果说线程A正在处于同步代码块中,相当于线程A还没有执行完,那么会将锁升级为轻量锁(偏向锁位为0,锁标志位为00),线程A继续执行同步代码块,而正在竞争的线程B会自动进行自旋。但如果说线程A刚好执行完同步代码块,此时会设置为无锁的状态,线程A,B会同时开始竞争。如下图:

 ​​​​​​

        假如此时锁升级为了轻量级锁,JVM会在每个线程的栈帧中创建用于存储锁记录的空间(Displaced Mark Word),若此时线程A想要获取轻量级锁,会把锁对象的MarkWord拷贝复制到自己的DMW里面,然后线程A再尝试利用CAS将锁对象中的MarkWord中的指向记录改为指向线程A栈中的Lock Record的指针,此时如果线程A的CAS失败了,就说明线程B正在占用此锁,线程A就会通过不断自旋来获取锁,等到线程B执行完后,线程B还要将轻量级锁释放,线程B使用CAS操作将DMW的内容重新复制回锁对象的Mark Word里面。如果此时有大量的线程涌入,参与竞争,一个线程自旋到一定的次数,锁就会会升级为重量级锁(偏向锁位为0,锁标志位为10),没拿到锁的线程会等待操作系统的调动,就不在主动的去抢占获取锁了。具体这个自旋次数在Java8之后是自适应自旋锁。

  • 线程如果自旋成功了,那下次自旋的最大次数会增加,因为JVM认为既然上次成功了,那么这一次也大概率会成功

  • 如果很少会自选成功,那么下次会减少自旋的次数甚至不自旋,避免CPU空转。

3.2几个需要说明的小问题?

1.JDK15废除了偏向锁

JDK15以后逐步废弃偏向锁,需要手动开启------->因为维护成本高。

2.MarkWord中指向记录在不同状态的指向不同

  • 偏向锁:MarkWord存储的是偏向的线程ID

  • 轻量锁:MarkWord存储的是指向线程栈中Lock Record的指针

  • 重量锁:MarkWord存储的是指向堆中锁的Monitor(监视器)对象,修改里面的owner来实现。

3.无锁会默认到偏向锁

实际上无锁是默认会自动升级为偏向锁的,但是启动时间有延迟,可以通过添加参数,让其在程序启动时立即启动。

4.锁升级后,hashcode去哪里了?

我们可以发现,hashcode值的位置和锁指向的内存位置会冲突,那么内部是怎么解决的呢——>

  • 在无锁的状态下,对象的hashcode()值存储在Mark Word中,此时它就再也无法进入到偏向锁状态了。

  • 如果已经在偏向锁状态下,才调用hashcode()方法,偏向锁的状态会被立即取消,锁会膨胀为重量级锁。

  • 在轻量级锁状态下,会在DMW中保存拷贝的Mark Word的值,释放锁后,会将这些信息重新写回到对象头的Mark Word中(相当于覆盖了)。

  • ObjectMonitor类里面有字段会记录非加锁状态下的Mark Word,锁释放后也会重新写回到对象头中的Mark Word中。

3.3JIT编译器对锁的优化

JIT对锁的优化分为锁消除和锁粗化,其实这两个概念挺乏味的。

3.3.1锁消除

简单来说就是,如果每个线程都拥有一把锁,那么我们写的加锁代码就毫无意义了,从JIT角度来看就是无视它了,消除了对锁的使用。示例代码:

public class LockClearUpDemo {static Object object = new Object();public void m1() {//锁消除问题,JIT会无视它,synchronized(o)每次new出来的,加锁就无意义了Object o = new Object();synchronized (o) {System.out.println("-----------hello LockClearUpDemo" + "\t" + o.hashCode() + "\t" + object.hashCode());}}public static void main(String[] args) {LockClearUpDemo lockClearUpDemo = new LockClearUpDemo();for (int i = 0; i < 10; i++) {new Thread(() -> {lockClearUpDemo.m1();}, String.valueOf(i)).start();}}
}

3.3.2锁粗化

如方法中多个同步块首尾相接,前后使用的都是同一个锁对象,那么JIT编译器会把这几个synchronized块合并为一个大块,加粗锁的范围。

public class LockBigDemo {static Object objectLock = new Object();public static void main(String[] args) {new Thread(() -> {synchronized (objectLock) {System.out.println("111111111111");}synchronized (objectLock) {System.out.println("222222222222");}synchronized (objectLock) {System.out.println("333333333333");}synchronized (objectLock) {System.out.println("444444444444");}//底层JIT的锁粗化优化synchronized (objectLock) {System.out.println("111111111111");System.out.println("222222222222");System.out.println("333333333333");System.out.println("444444444444");}}, "t1").start();}
}

4.Synchronized的具体实现

        线程代码进入到Synchronized代码块时会自动获取锁对象,这时其他线程访问时会被阻塞,直到Synchroinzed代码块执行完毕或抛出异常,调用wait()方法都会释放锁对象。在进入Synchronized代码块时会将主内存的变量读取到自己的工作内存,在退出的时候会把工作内存的更新值写入到主内存。Java中Synchronized通过在锁对象的对象头设置标记,达到获取锁和释放锁的目的。

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

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

相关文章

Legion Y9000X IRH8 2023款(82Y3)原装出厂OEM预装Windows11系统

lenovo联想电脑笔记本拯救者原厂win11系统镜像 下载链接&#xff1a;https://pan.baidu.com/s/15G01j7ROVqOFOETccQSKHg?pwdt1ju 系统自带所有驱动、出厂主题壁纸、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;32G或以上的U盘 文件格式&#xff1a;ISO…

Vue 3 学习 源码解读

该文章内容为以下视频的学习笔记&#xff1a; 前言_哔哩哔哩_bilibili前言是秋招解决方案&#xff1a;深入 Vue3 源码&#xff0c;带你彻底打通 Vue3 源码面试的第1集视频&#xff0c;该合集共计13集&#xff0c;视频收藏或关注UP主&#xff0c;及时了解更多相关视频内容。htt…

JVM上篇之虚拟机与java虚拟机介绍

目录 虚拟机 java虚拟机 简介 特点 作用 位置 整体结构 类装载子系统 运行时数据区 java执行引擎 Java代码执行流程 jvm架构模型 基于栈式架构 基于寄存器架构 总结 jvm的生命周期 1.启动 2.执行 3.退出 JVM的发展历程 虚拟机 所谓虚拟机&#xff0c;指的…

【微服务】RedisSearch 使用详解

目录 一、RedisJson介绍 1.1 RedisJson是什么 1.2 RedisJson特点 1.3 RedisJson使用场景 1.3.1 数据结构化存储 1.3.2 实时数据分析 1.3.3 事件存储和分析 1.3.4 文档存储和检索 二、当前使用中的问题 2.1 刚性数据库模式限制了敏捷性 2.2 基于磁盘的文档存储导致瓶…

数字三角形加强版题解(组合计数+快速幂+逆元)

Description 一个无限行的数字三角形&#xff0c;第 i 行有 i 个数。第一行的第一个数是 1 &#xff0c;其他的数满足如下关系&#xff1a;如果用 F[i][j] 表示第 i 行的第 j 个数&#xff0c;那么 F[i][j]A∗F[i−1][j]B∗F[i−1][j−1] &#xff08;不合法的下标的数为 0 &a…

Springboot项目log4j与logback的Jar包冲突问题

异常信息关键词&#xff1a; SLF4J: Class path contains multiple SLF4J bindings. ERROR in ch.qos.logback.core.joran.spi.Interpreter24:14 - no applicable action for [properties], current ElementPath is [[configuration][properties]] 详细异常信息&#xff1a…

水波纹文字效果动画

效果展示 CSS 知识点 text-shadow 属性绘制立体文字clip-path 属性来绘制水波纹 工具网站 CSS clip-path maker 效果编辑器 页面整体结构实现 使用多个 H2 标签来实现水波纹的效果实现&#xff0c;然后使用clip-path结合动画属性一起来进行波浪的起伏动画实现。 <div …

盒子模型的基础

盒子模型 边框&#xff08;border&#xff09; border可以设置元素的边框&#xff0c;边框分成三部分&#xff0c;边框的&#xff08;粗细&#xff09;边框的样式&#xff0c;边框的颜色 <style>div {width: 100px;height: 100px;border-width: 200;border-style: 边框…

应用层协议——DNS、DHCP、HTTP、FTP

目录 1、DNS 协议 1-1&#xff09;Hosts 文件 1-2&#xff09;DNS 系统 1-3&#xff09;域名的组成、分类和树状结构 1-4&#xff09;DNS 域名服务器类型 1-5&#xff09;DNS 查询方式 1-6&#xff09;DNS 域名解析的一般步骤 1-7&#xff09;对象类型与资源记录 2、D…

ELK整合springboot(第二课)

一、创建一个springboot的项目 pom文件如下&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLo…

深入解析 const 关键字:指针、参数、返回值和类成员函数

文章目录 const 关键字的理解一、 修饰普通类型的变量二、const 修饰指针变量三、const 作参数传递 和 函数返回值&#xff08;1&#xff09;const 修饰函数参数&#xff08;2&#xff09;const 修饰函数返回值 四、const修饰类成员函数结尾 const 关键字的理解 const 在 C 中…

基于SVM+TensorFlow+Django的酒店评论打分智能推荐系统——机器学习算法应用(含python工程源码)+数据集+模型(一)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python环境TensorFlow 环境方法一方法二 安装其他模块安装MySQL 数据库 模块实现1. 数据预处理1&#xff09;数据整合2&#xff09;文本清洗3&#xff09;文本分词 相关其它博客工程源代码下载其它资料下载 前言 本项目以支…