你知道Synchronized作用是同步加锁,可你知道它在JVM中是如何实现的吗?

分布式场景下是否适用 synchronized 加锁机制? 综上所述,在分布式系统中不能直接使用来实现同步,需要根据具体的业务场景选择合适的分布式锁方案。 阅读详情

在这里插入图片描述

点关注,不迷路;持续更新Java架构相关技术及资讯热文!!!

眼尖的朋友可能还看见了此博客页面左上角还有惊喜哟

友情提示:

本文内容涉及JVM底层,文章烧脑,请谨慎阅读!

我们可以利用synchronized关键字来对程序进行加锁。它既可以用来声明一个synchronized代码块,也可以直接标记静态方法或者实例方法。

当谈到synchronized时,我们有必要了解字节码中的monitorenter和monitorexit指令。

这两种指令均会消耗操作数栈上的一个引用类型的元素(也就是 synchronized关键字括号里的引用),作为所要加锁解锁的锁对象。

下面我们将深入了解Synchronized在JVM底层的实现原理。

考察以下的代码:

查看这个代码编译后的字节码,我就直接用下面这张图解释了。

ps:截图截得不太好,下面有点没截到,大家凑合看看:

你可能会留意到,上面的字节码中包含一个 monitorenter 指令以及多个 monitorexit 指令。

这是因为 Java 虚拟机需要确保所获得的锁在正常执行路径以及异常执行路径上都能够被解锁。

大家可以看我的注释,自己思考一下,应该都能看懂。

应该注意,如果用synchronized标记方法,你会看到字节码中方法的访问标记包括ACC_SYNCHRONIZED。

该标记表示在进入该方法时,Java 虚拟机需要进行monitorenter操作。

而在退出该方法时,不管是正常返回,还是向调用者抛异常,Java 虚拟机均需要进行monitorexit操作。

用两张图一看就懂。

可以看到,在0号字节码处就返回了。

这里有人可能问了,这里没有调用monitorenter和monitorexit指令啊?怎么实现的加锁?

要注意,这里monitorenter 和 monitorexit 操作所对应的锁对象是隐式的。

对于实例方法来说,这两个操作对应的锁对象是 this;对于静态方法来说,这两个操作对应的锁对象则是所在类的Class实例。

我们先来介绍Synchronized的重入的实现机理。

可以认为每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。

当执行monitorenter时,如果目标锁对象的计数器为0,那么说明它没有被其他线程所持有。

Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。

在目标锁对象的计数器不为 0 的情况下,如果锁对象的持有线程是当前线程,那么 Java 虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。

当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为0,代表锁已被释放。

这就是锁的重入的实现机理。

说完了这个实现机理,我们来探究具体的锁实现。

首先谈谈重量级锁,重量级锁是 Java 虚拟机中最为基础的锁实现。

在这种状态下,Java 虚拟机会阻塞加锁失败的线程,并且在目标锁被释放的时候,唤醒这些线程。在Linux中,这是通过pthread库的互斥锁来实现的。

此外,这些操作将涉及系统调用,需要从操作系统的用户态切换至内核态,其开销非常之大。

为了尽量避免昂贵的线程阻塞、唤醒操作,Java虚拟机会在线程进入阻塞状态之前,以及被唤醒后竞争不到锁的情况下,进入自旋状态,在处理器上空跑并且轮询锁是否被释放。

如果此时锁恰好被释放了,那么当前线程便无须进入阻塞状态,而是直接获得这把锁。

下面我将介绍自适应自旋的概念,刚才说了自旋是什么,但是自旋很耗费资源,所以我们可以根据以往自旋等待时是否能够获得锁,来动态调整自旋的时间(循环数目)。

所以Synchronized是否公平这个问题可以休矣,为什么呢?

处于阻塞状态的线程,并没有办法立刻竞争被释放的锁。然而,处于自旋状态的线程,则很有可能优先获得这把锁。所以Synchronized不是公平的。

我们再介绍轻量级锁,针对多个线程在不同的时间段请求同一把锁,也就是说没有锁竞争。

针对这种情形,Java 虚拟机采用了轻量级锁,来避免重量级锁的阻塞以及唤醒。

在介绍轻量级锁的原理之前,我们先来了解一下Java虚拟机是怎么区分轻量级锁和重量级锁的。

简单的说,对象头中有一个标记字段。它的最后两位便被用来表示该对象的锁状态,其中:

  • 00代表轻量级锁
  • 01代表无锁(或偏向锁)
  • 10代表重量级锁
  • 11则跟垃圾回收算法的标记有关。

当进行加锁操作时,Java虚拟机会判断是否已经是重量级锁。

如果不是,它会在当前线程的当前栈桢中划出一块空间,作为该锁的锁记录,并且将锁对象的标记字段复制到该锁记录中。

然后,Java 虚拟机会尝试用 CAS 操作替换锁对象的标记字段。

各位有兴趣可以了解一下JVM的CAS在X86机器上的实现,是汇编指令lock cmpxhcg。

这里我简单介绍一下,CAS 是一个原子操作,它会比较目标地址的值是否和期望值相等,如果相等,则替换为一个新的值。

假设当前锁对象的标记字段为 X…XYZ,Java 虚拟机会比较该字段是否为 X…X01。

如果是,则替换为刚才分配的锁记录的地址。由于内存对齐的缘故,它的最后两位为 00。此时,该线程已成功获得这把锁,可以继续执行了。

如果不是 X…X01,那么有两种可能:

  • 第一,该线程重复获取同一把锁。此时,Java 虚拟机会将0加入锁记录,以代表该锁被重复获取。
  • 第二,其他线程持有该锁。此时,Java 虚拟机会将这把锁膨胀为重量级锁,并且阻塞当前线程。

你可以将一个线程的所有锁记录想象成一个栈结构,每次加锁压入一条锁记录,解锁弹出一条锁记录,当前锁记录指的便是栈顶的锁记录。

当进行解锁操作时,如果当前锁记录的值为 0,则代表重复进入同一把锁,直接返回即可。

若当前锁记录不是0,Java 虚拟机会尝试用 CAS 操作,比较锁对象的标记字段的值是否为当前锁记录的地址。

如果是,则替换为锁记录中的值,也就是锁对象原本的标记字段。此时,该线程已经成功释放这把锁。

如果不是,则意味着这把锁已经被膨胀为重量级锁。此时,Java 虚拟机会进入重量级锁的释放过程,唤醒因竞争该锁而被阻塞了的线程。

下面我们介绍偏向锁,偏向锁针对的是从始至终只有一个线程请求某一把锁。是轻量级锁的更进一步的乐观情况。

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

这里介绍一下epoch的概念,每个类中维护一个epoch值,你可以理解为这个类所有实例对象的第几代偏向锁。

当设置偏向锁时,Java 虚拟机需要将该epoch值复制到锁对象的标记字段中。我们规定,你加的偏向锁的代数高,是可以把代数低的PK下去的。

接下来我给你讲的过程,你就知道为什么要这么设计了。

我们先从偏向锁的撤销讲起。

当请求加锁的线程和锁对象标记字段保持的线程地址不匹配时(而且epoch即代数必须相等,如若不等,那么当前线程可以将该锁重偏向至自己,因为新的epoch的代数肯定要高于以前的代数),Java 虚拟机需要撤销该偏向锁。

这个撤销过程非常麻烦,它要求持有偏向锁的线程到达安全点,再将偏向锁替换成轻量级锁。

在宣布某个类的偏向锁失效时,Java 虚拟机实则将该类的epoch值加 1,表示之前那一代的偏向锁已经失效。而新设置的偏向锁则需要使用类中的最新epoch代数来加锁。

为了保证当前持有偏向锁并且已加锁的线程不至于因此丢锁,Java虚拟机需要遍历所有线程的Java栈,找出该类已加锁的实例,并且将它们标记字段中的 epoch值加1。该操作需要所有线程处于安全点状态。

所以有专家近年来提出,偏向锁在锁竞争激烈的情况下,非但不能优化性能,反而可能伤害应用性能。

如果总撤销数超过另一个阈值(对应 Java 虚拟机参数-XX:BiasedLockingBulkRevokeThreshold,默认值为 40),那么 Java 虚拟机会认为这个类已经不再适合偏向锁。

此时,Java 虚拟机会撤销该类实例的偏向锁,并且在之后的加锁过程中直接为该类实例设置轻量级锁。

End
BAT架构经验倾囊相授

Nmap源码分析(端口扫描) 端口扫描是Nmap的核心功能,用于确定目标机的端口状态(开放、关闭、过滤等),也为Nmap的服务与版本扫描、OS扫描、脚本扫描提供基本的指引信息。所以,深入理解端口扫描的实现对分析其他的扫描方式很有帮助。 1简单回顾   首先简要回顾一下端口扫描的原理以及命令行选项。 1.1扫描原理   Nmap提供的10多种类型的端口扫描方法,如TCP SYN/ACK/FIN/Xmas/NULL/ Windows/Connect,FTP Bounce, Idle scan, UDP port u... 阅读详情

相关推荐

unity游戏开发日志(一)将mmd模型导入unity,并解决材质丢失的问题

unity音乐游戏开发,导入角色模型并解决材质问题

yizhigou1的博客 1762

【嗅探底层】你知道Synchronized作用是同步加锁,可你知道它在JVM中是如何实现的吗?...

​本文系公众号石杉的架构笔记的读者投稿作者:李瑞杰目前任职于阿里巴巴,资深JVM研究人员友情提示:本文内容涉及JVM底层,文章烧脑,请谨慎阅读!我们可以利用synchronized关键字来对程序进行加锁。它既可以用来声明一个synchronized代码块,也可以直接标记静态方法或者实例方法。当谈到synchronized时,我们有必要了解字节码中的monitorenter和monitorexit指...

weixin_33748818的博客 325

NI数据采集卡测电流的三种‘姿势’:直接连接、外接分流电阻还是用电流互感器?看完这篇不再纠结

本文全面解析了NI数据采集卡在电流测量中的三种主流方案:直接连接、外接分流电阻和电流互感器。通过对比精度、带宽、隔离等关键参数,结合NI-DAQmx软件生态,为工业自动化、电力监测等场景提供选型指南和实战技巧,帮助工程师优化测量系统设计。

weixin_30882895的博客 421

java多线程之synchronized锁的实现原理

java中synchronized是由JVM层实现的锁,而Lock的实现完全是由JAVA层实现,jdk 1.5之前synchronized是重量级锁实现,效率比较低,经过JVM·的优化,jdk1.5版本及后synchronbized的锁效率已经和Lock差不多。 synchronized是通过monitorEnter和monitorExit的JVM指令实现的, 下面通过一个简单demo展示下 public static void main(String[] args) { synchroni

m0_58554082的博客 262

Wezterm的安装和简单配置

希望 wezterm 的主题配色,保持和 powershell 默认配色一致,也就是 Compell 主题。目的是在频繁使用 wezterm 时,原本的根据颜色判断文件信息(尤其是WSL里的Linux下,文件和目录颜色)依然可以使用。wezterm 默认用 cmd.exe, 我是 win11,希望默认用 powershell。

baiyu33的博客 4269

Vivado固化程序与Flash型号添加实战指南

本文详细介绍了Vivado程序固化到Flash芯片的完整流程,包括基础操作、Flash型号添加方法及疑难问题排查。针对Vivado官方支持列表缺失的Flash型号,提供了手动添加配置的详细步骤和参数说明,并分享了高级技巧与安全操作建议,帮助开发者高效完成FPGA程序固化。

weixin_29291573的博客 566

一篇就够,synchronized原理详解

synchronized详解 在多线程同时访问一个共享变量时,volatile是一个轻量级的解决方案,但是volatile只能修饰一个变量并且不保证原子性。在Java中还提供了两种方式实现同步互斥访问:synchronized和Lock 其实这些本质上就是加锁, 加锁目的:序列化访问临界资源,即同一时刻只能有一个线程访问临界资源(同步互斥访问) 不过有一点需要区别的是:当多个线程执行一个方法时,该方法内部的局部变量并不是临界资源,因为这些局部变量是在每个线程的 私有栈中,因此不具有共享性,不会导致线程安全

小派师兄 1万+

Cadence笔记--原理图导入PCB

根据PCB尺寸画PCB外观尺寸,这里注意X\Y不能超过指定的区域,X\Y的最大值在setuo的grids里设置,画完后保存并关闭工程然后重新打开(不理解为何)由于只画了一个PMU6050,所以这里只有一个器件。工程选择board,选择存储路径和名称。6、重新网格导入,保证网表导入无误;注意网表保存的路径。

weixin_51418325的博客 1959

【多线程】synchronized 加锁原理详解

synchronized基本特点,synchronized加锁工作过程,偏向锁,轻量级锁,重量级锁, sunchronized优化操作,锁消除,锁粗化,相关面试题

ZXT的博客 2075

骑砍Ⅱ霸主MOD开发(16)-FaceKey&BannerKey

摘要:本文介绍了FaceKey人物模型和BannerKey旗帜模型的生成流程。FaceKey通过序列化参数生成人物模型,包括头部(脸部、嘴、睫毛、眼睛)、眉毛、头发、身体和躯干等组件,每个部分通过特定标签和骨骼绑定实现。BannerKey则通过解析参数生成旗帜模型,包括模型、贴图和颜色等元素。两种模型都采用参数化设计和XML配置文件实现模型生成与定制。

骑砍战团&霸主MOD开发 2286

【C2000系列DSP的双向DC-DC电源】基于TMS320F28034的DC-DC项目开发流程

理解DC-DC 双向电源(基于 TMS320F28034)的开发流程,需要从功能拆解、开发步骤、验证 / 测试项

昔时扬尘处 323

synchronized锁原理详解

在理解锁实现原理之前先了解一下Java的对象头和Monitor,在JVM中,对象是分成三部分对象头实例数据对其填充实例数据和对其填充与synchronized无关,对象头是我们需要关注的重点,它是synchronized实现锁的基础,因为synchronized申请锁、上锁、释放锁都与对象头有关。......

java521666的博客 1094

Transformer实战(33)——高效自注意力机制

在本节中,我们学习了如何在硬件资源受限的情况下减轻运行大模型的负担,介绍了高效的稀疏 Transformer 模型,通过近似技术(如 Linformer、BigBird 和 Performer) 用稀疏矩阵替代全自注意力矩阵。并观察它们在各种基准测试中的表现,例如计算复杂度和内存复杂度。结果表明,这些方法可以在不牺牲性能的情况下将平方级复杂度降低到线性复杂度。

盼小辉丶的博客 3051

[并发编程]你知道Synchronized作用是同步加锁,可你知道它在JVM中是如何实现的吗?

我们可以利用synchronized关键字来对程序进行加锁。它既可以用来声明一个synchronized代码块,也可以直接标记静态方法或者实例方法。 当谈到synchronized时,我们有必要了解字节码中的monitorenter和monitorexit指令。 这两种指令均会消耗操作数栈上的一个引用类型的元素(也就是 synchronized关键字括号里的引用),作为所要加锁解锁...

疯一样的女子 486

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

怎么知道java涉及同步_【嗅探底层】你知道Synchronized作用是同步加锁,可你知道它在JVM中是如何实现的吗?...

目前任职于阿里巴巴,资深JVM研究人员友情提示:本文内容涉及JVM底层,文章烧脑,请谨慎阅读!我们可以利用synchronized关键字来对程序进行加锁。它既可以用来声明一个synchronized代码块,也可以直接标记静态方法或者实例方法。当谈到synchronized时,我们有必要了解字节码中的monitorenter和monitorexit指令。这两种指令均会消耗操作数栈上的一个引用类型的元...

weixin_31812189的博客 89

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

synchronized底层实现原理及锁优化

一、概述 1、synchronized作用 原子性:synchronized保证语句块内操作是原子的 可见性:synchronized保证可见性(通过“在执行unlock之前,必须先把此变量同步回主内存”实现) 有序性:synchronized保证有序性(通过“一个变量在同一时刻只允许一条线程对其进行lock操作”) 2、synchronized的使用 修饰实例方法,对当前实例对象加锁 修饰...

Medlen 7万+

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

jvm-Synchronized的实现原理

synchronized,是Java中用于解决并发情况下数据同步访问的一个很重要的关键字。当我们想要保证一个共享资源在同一时间只会被一个线程访问到时,我们可以在代码中使用synchronized关键字对类或者对象加锁。那么,本文来介绍一下synchronized关键字的实现原理是什么。在阅读本文之间,建议先看下Java虚拟机是如何执行线程同步的。 反编译 众所周知,在Java中,synchr...

wojiao228925661的博客 437

JVM synchronized 详细探究

首先,同步在JVM中是通过monitor进入和monitor退出来实现的。 1、synchronized 方法   对于synchronized方法,同步作为方法调用和返回的一部分被隐式执行,并不是使用monitorenter和monitorexit来实现方法同步。而是通过运行时常量池中方法对应的ACC_SYNCHRONIZED标志来实现同步。 1.1 synchronized 方法怎么被识别? ...

初心江湖路的博客 302

11.synchronized底层是怎么通过monitor进行加锁的?

jvm中的monitor是个什么东西?synchronized底层是怎么通过monitor进行加锁的?

chenzengnian123的博客 2084

synchronized用法加锁原理

由于方法没有设置锁,两个线程都可以进入方法内,线程A进入代码块之后,线程B尝试进入代码块失败,因为线程A已经获得synchronized后括号内对象的锁,线程B只有在线程A退出同步代码块之后,释放对象锁,线程B才可以进入同步代码块,在此之前,线程B只能阻塞等待。一个类中定义一个静态同步方法,创建两个含有静态同步方法对象实例,开启两个线程,在线程A中,实例1调用静态同步方法,并在方法中睡眠5秒钟,同时实例2也调用静态同步方法。线程A进入同步代码块之后,线程B只有在线程A退出同步代码块之后才进入了同步代码块。

huhu123444的博客 1238

Synchronized使用、 java对象在JVM内存模型中的构成、 synchronized同步代码块的底层原理、 synchronized同步方法的底层原理

文章目录参考Synchronized 使用java对象在 JVM内存模型 中 的构成synchronized 同步代码块 的底层原理synchronized 同步方法 的底层原理 参考 https://blog.youkuaiyun.com/qq_35190492/article/details/106180781 https://www.cnblogs.com/lukelook/p/9946065.html https://baijiahao.baidu.com/s?id=1612142459503895416 S

LYPvvvLYP的博客 287

synchronized实现原理分析 - 字节码层面分析jvm底层如何实现Synchronized

开头我们先来复习一下synchronized的使用方法。 第一种在实例方法上使用表示是对当前实例的加锁: public synchronized void myMethod2(){ System.out.println("hello world"); } 第二种是用在类方法上表示对当前类的Class对象加锁: public static synchron...

Crown的博客 675

synchronized实现线程同步的用法和实现原理

作用和用法 在多线程对共享资源进行并发访问方面,JDK提供了synchronized关键字来进行线程同步,实现多线程并发访问的线程安全。synchronized的作用主要体现在三个方面:(1)确保线程互斥地访问同步代码;(2)保证共享变量的线程可见性;(3)禁止指令重排。其中(2)和(3)相当于volatile关键字的作用。 sychronized可以用在(1)静态方法,(2)普通成员方法...

源码之下,了无秘密 6135

重大发现,AQS加锁机制竟然跟Synchronized有惊人的相似

AQS跟的加锁流程是一样的,都是通过同步队列和条件队列实现的,阻塞状态的线程被放到同步队列中,等待状态的线程被放到条件队列中,从条件队列唤醒的线程又被转移到同步队列末尾,一块竞争锁。看完AQS加锁流程,还没有人不懂AQS的?下篇文章再讲一下AQS加锁具体的源码实现。里面有很多精巧的设计,值得我们学习。比如:为什么同步队列要设计成双向链表?而条件队列要设计成单链表?为什么AQS加锁性能这么好(乐观锁CAS使用)?同步队列和条件队列中节点怎么用一个对象实现?释放锁后,怎么唤醒同步队列中线程?

chuixue24的博客 467

【JUC】——synchronized应用、作用及实现原理

前言 在 Java 中,关键字 synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时我们还应该注意到synchronized另外一个重要的作用,synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代Volatile功能),这点确实也是很重要的。 三种应...

我爱吃土豆 6631
上一篇: Java程序员必备——Java中高级面试题【BATJ】
下一篇: 如何快速鉴别“菜鸟”级别和“大神”级别的程序员
Java_No01
博客等级 码龄7年 17粉丝 36原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值