Synchronized的使用及原理总结
本文为博主原创,未经允许不得转载
Synchronized的使用总结:
1.作用
原理
synchronized 的锁膨胀升级过程
对象的内存布局
锁的消除及逃逸分析
synchronized的方法锁,对象锁,类锁的作用范围
1.作用
Java 中,提供了两种方式来实现同步互斥访问:synchronized 和 Lock
实现序列化访问临界资源,即同一时刻只能有一个线程访问临界资源(同步互斥访问)
2. 原理
synchronized内置锁是一种对象锁(锁的是对象而非引用),作用粒度是对象,可以用来实现对临界资源的同步互斥访问,是可重入的。
加锁的方式:
1、同步实例方法,锁是当前实例对象
2、同步类方法,锁是当前类对象
3、同步代码块,锁是括号里面的对象
synchronized底层原理
synchronized是基于JVM内置锁实现,通过内部对象Monitor(监视器锁)实现,基于进入与退出Monitor对象实现方法与代码 块同步,监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁)实现,它是一个重量级锁性能较低。JVM内置锁在1.5之后版本做了重大的优化,如锁粗化(Lock Coarsening)、锁消除(Lock Elimination)、轻量级锁(Lightweight Locking)、偏向锁(Biased Locking)、适应性自旋(Adaptive Spinning)等技术来减少锁操作的开销,,内置锁的并发性能已经基本与 Lock持平。
synchronized关键字被编译成字节码后会被翻译成monitorenter 和 monitorexit 两条指令分别在同步块逻辑代码的起始位置 与结束位置。
每个同步对象都有一个自己的Monitor(监视器锁),加锁过程如下图所示:
Monitor监视器锁
任何一个对象都有一个Monitor与之关联,当且一个Monitor被持有后,它将处于锁定状态。Synchronized在JVM里的实现都是 基于进入和退出Monitor对象来实现方法同步和代码块同步。方法的同步并没有通过指令 monitorenter 和 monitorexit 来完成。不过相对于普通方法,其常量池中多了 ACC_SYNCHRONIZED 标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个 monitor对象。 两种同步方式本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。两个指令的执行是JVM通过调用操作系统的互斥原语mutex来实现,被阻塞的线程会被挂起、等待重新调度**,会导致“用户态和内核态”两个态之间来回切换,对性能有较大影响。
synchronized加锁是加在对象上,对象是如何记录锁状态的呢?答案是锁状态是被记录在每个对象 的对象头(Mark Word)中,看下对象的内存布局。
对象的内存布局
HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充 (Padding)。
对象头:比如 hash码,对象所属的年代,对象锁,锁状态标志,偏向锁(线程)ID,偏向时间,数组长度(数组对象) 等。它是实现轻量级锁和偏向锁的关 键。Java对象头一般占有2个机器码(在32位虚拟机中,1个机器码等于4字节,也就是32bit,在64位虚拟机中,1个机器码 是8个字节,也就是64bit),但是 如果对象是数组类型,则需要3个机器码,因为JVM虚拟机可以通过Java对象的元数据信 息确定Java对象的大小,但是无法从数组的元数据来确认数组的大小,所以用一块来记录数组长度。
实例数据:存放类的属性数据信息,包括父类的属性信息;
对齐填充:由于虚拟机要求 对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐;
synchronized 的锁膨胀升级过程
锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重 量级锁,但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级。
在大多数情况下,锁不仅不存在多线程竞 争,而且总是由同一线程多次获得,因此为了减少同一线程获取锁(会涉及到一些CAS操作,耗时)的代价而引入偏向锁。偏向锁的核心 想是,如果一个线程获得了锁,那么锁就进入偏向模式,此时Mark Word 的结构也变为偏向锁结构,当这个线程再次请求锁时, 无需再做任何同步操作,即获取锁的过程,这样就省去了大量有关锁申请的操作,从而也就提供程序的性能。
对于没有锁竞争 的场合,偏向锁有很好的优化效果,毕竟极有可能连续多次是同一个线程申请相同的锁。但是对于锁竞争比较激烈的场合,偏向锁就 失效了,因为这样场合极有可能每次申请锁的线程都是不相同的,因此这种场合下不应该使用偏向锁,否则会得不偿失.
轻量级锁
倘若偏向锁失败,虚拟机并不会立即升级为重量级锁,它还会尝试使用一种称为轻量级锁的优化手段.轻量级锁能够提升程序性能的依据是“对绝大部分的锁,在整个同步周期内都不存在竞 争”,注意这是经验数据。需要了解的是,轻量级锁所适应的场景是线程交替执行同步块的场合,如果存在同一时间访问同一锁的场 合,就会导致轻量级锁膨胀为重量级锁。
自旋锁
轻量级锁失败后,虚拟机为了避免线程真实地在操作系统层面挂起,还会进行一项称为自旋锁的优化手段。在大多数情 况下,线程持有锁的时间都不会太长,如果直接挂起操作系统层面的线程可能会得不偿失,毕竟操作系统实现线程之间的切换时需要 从用户态转换到核心态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高.虚拟机会让当前想要获取锁的线程做几个空循环,一般不会太久,在经过若干次循环后,如果得到锁,就顺利进入临界区。如果还不能获得锁,那就会将线程在操作系统层面挂起,这就是 自旋锁的优化方式,这种方式确实也是可以提升效率的。最后没办法也就只能升级为重量级锁了。
锁消除
消除锁是虚拟机另外一种锁的优化,这种优化更彻底,Java虚拟机在JIT编译时(可以简单理解为当某段代码即将第一次被执行时 进行编译,又称即时编译),通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过这种方式消除没有必要的锁,可以 节省毫无意义的请求锁时间,如下StringBuffer的append是一个同步方法,但是在add方法中的StringBuffer属于一个局部变量,并 且不会被其他线程所使用,因此StringBuffer不可能存在共享资源竞争的情景,JVM会自动将其锁消除。锁消除的依据是逃逸分析的数据支持。
锁粗化
将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁,避免频繁的加锁解锁操作。
Synchronized的使用及原理总结的更多相关文章
- java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析
java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...
- Java synchronized 关键字的实现原理
数据同步需要依赖锁,那锁的同步又依赖谁?synchronized给出的答案是在软件层面依赖JVM,而Lock给出的方案是在硬件层面依赖特殊的CPU指令,大家可能会进一步追问:JVM底层又是如何实现sy ...
- 解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度-美团)
还有其他的锁,如果想要了解,参考:JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁, 用synchronized实现ReentrantLock 美团面试题参考:使用synchronized ...
- Java并发编程知识点总结Volatile、Synchronized、Lock实现原理
Volatile关键字及其实现原理 在多线程并发编程中,Volatile可以理解为轻量级的Synchronized,用volatile关键字声明的变量,叫做共享变量,其保证了变量的“可见性”以及“有序 ...
- synchronized的底层实现原理
转自:http://www.cnblogs.com/paddix/p/5367116.html 如果对上面的执行结果还有疑问,也先不用急,我们先来了解Synchronized的原理,再回头上面的问题就 ...
- synchronized实现原理
线程安全是并发编程中的重要关注点,应该注意到的是,造成线程安全问题的主要诱因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据.因此为了解决这个问题,我们可能需要这样一个方案, ...
- jdk1.8源码Synchronized及其实现原理
一.Synchronized的基本使用 关于Synchronized在JVM的原理(偏向锁,轻量级锁,重量级锁)可以参考 : http://www.cnblogs.com/dennyzhangdd/ ...
- 深入理解Java并发之synchronized实现原理
深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入 ...
- 深入理解多线程(一)——Synchronized的实现原理
synchronized,是Java中用于解决并发情况下数据同步访问的一个很重要的关键字.当我们想要保证一个共享资源在同一时间只会被一个线程访问到时,我们可以在代码中使用synchronized关键字 ...
- 深入理解并发编程之----synchronized实现原理
版权声明:本文为博主原创文章,请尊重原创,未经博主允许禁止转载,保留追究权 https://blog.csdn.net/javazejian/article/details/72828483 [版权申 ...
随机推荐
- JOISC2020题解
\(\text{By DaiRuiChen007}\) Contest Link A. Building 4 Problem Link 题目大意 给 \(2n\) 个数对 \((a_i,b_i)\), ...
- STM32CubeMX教程3 GPIO输入 - 按键响应
1.准备材料 开发板(STM32F407G-DISC1) ST-LINK/V2驱动 STM32CubeMX软件(Version 6.10.0) keil µVision5 IDE(MDK-Arm) 2 ...
- Python——第二章:字符串操作——格式化
1. 字符串的格式化问题 举例:要按照如下格式输出一句话 我叫xxx, 我住在xxxx, 我今年xx岁, 我喜欢做xxxxx 这里首先引入占位符概念: %s 占位字符串%d 占位整数%f 占位小数 因 ...
- Windows 7更新失败的解决方法
你好,1.在开始菜单中点击运行,→输入"services.msc"→找到"windows update"右击选择"停止":2.进入C:\wi ...
- 如何利用动态配置中心在JavaAgent中实现微服务的多样化治理
本文分享自华为云社区<如何利用动态配置中心在JavaAgent中实现微服务的多样化治理>,作者:华为云开源 . 一.前言 随着JavaAgent在微服务治理方面的广泛应用和发展,我们可以在 ...
- 技术实践丨手把手教你使用MQTT方式对接华为IoT平台 华为云开发者社区
摘要:本文主要讲述使用MQTT方式对接华为云IoT平台的具体过程. 使用的方案:目标板为STM32L431BearPI(带E53扩展板); TCPIP功能由开发板的ESP8266提供:MQTT使用Pa ...
- 华为云FusionInsight MRS在金融行业存算分离的实践
摘要:华为云FusionInsight MRS的大数据存算分离解决方案,实现资源价值最大化,存储与计算资源全面云化.灵活配置.弹性伸缩,降本增效. 在大数据.云计算.5G.AI等技术日新月异,数字经济 ...
- 华为云GaussDB深耕数字化下半场,持续打造数据库根技术
摘要:华为云数据库CTO庄乾锋携华为云数据库多位技术专家和优秀合作伙伴共同参与DTCC2021大会并发表了重要主题演讲. 10月18日,以"数造未来"为主题的第12届中国数据库技术 ...
- 破解数据匮乏现状:纵向联邦学习场景下的逻辑回归(LR)
摘要:主要介绍了华为云可信智能计算服务(TICS)采用的纵向联邦逻辑回归(LR)方案. 本文分享自华为云社区<纵向联邦学习场景下的逻辑回归(LR)>,作者: 汽水要加冰. 海量训练数据是人 ...
- JavaScript继承的实现方式:原型语言对象继承对象原理剖析
面向对象编程:继承.封装.多态. 对象的继承:A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法.这对于代码的复用是非常有用的. 在经典的面向对象语言中,您可能倾向于定义类对象,然后您 ...