JAVA锁的膨胀过程和优化(阿里)
阿里的人问什么是锁膨胀,答不上来,回来做了总结:
关于锁的膨胀,synchronized的原理参考:深入分析Synchronized原理(阿里面试题)
首先说一下锁的优化策略。
1,自旋锁
自旋锁其实就是在拿锁时发现已经有线程拿了锁,自己如果去拿会阻塞自己,这个时候会选择进行一次忙循环尝试。也就是不停循环看是否能等到上个线程自己释放锁。这个问题是基于一个现实考量的:很多拿了锁的线程会很快释放锁。因为一般敏感的操作不会很多。当然这个是一个不能完全确定的情况,只能说总体上是一种优化。
举个例子就好比一个人要上厕所发现厕所里面有人,他可以:1,等一小会。2,跑去另外的地方上厕所。等一小会不一定能等到前一个人出来,不过如果跑去别的厕所的花费的时间肯定比等一小会结果前一个人出来了长。当然等完了结果那个人没出来还是要跑去别的地方上厕所这是最慢的。
然后是基于这种做法的一个优化:自适应自旋锁。也就是说,第一次设置最多自旋10次,结果在自旋的过程中成功获得了锁,那么下一次就可以设置成最多自旋20次。道理是:一个锁如果能够在自旋的过程中被释放说明很有可能下一次也会发生这种事。那么就更要给这个锁某种“便利”方便其不阻塞得锁(毕竟快了很多)。同样如果多次尝试的结果是完全不能自旋等到其释放锁,那么就说明很有可能这个临界区里面的操作比较耗时间。就减小自旋的次数,因为其可能性太小了。
2,锁粗化
试想有一个循环,循环里面是一些敏感操作,有的人就在循环里面写上了synchronized关键字。这样确实没错不过效率也许会很低,因为其频繁地拿锁释放锁。要知道锁的取得(假如只考虑重量级MutexLock)是需要操作系统调用的,从用户态进入内核态,开销很大(阿里面试)。于是针对这种情况也许虚拟机发现了之后会适当扩大加锁的范围(所以叫锁粗化)以避免频繁的拿锁释放锁的过程。
3,锁消除
通过逃逸分析发现其实根本就没有别的线程产生竞争的可能(别的线程没有临界量的引用),而“自作多情”地给自己加上了锁。有可能虚拟机会直接去掉这个锁。
4,偏向锁和轻量级锁
这两个锁既是一种优化策略,也是一种膨胀过程所以一起说。首先它们的关系是:最高效的是偏向锁,尽量使用偏向锁,如果不能(发生了竞争)就膨胀为轻量级锁,这样优化的效率不如原来高不过还是一种优化(对比重量级锁而言)。所以整个过程是尽可能地优化。
首先说说偏向锁。
HotSpot的研究人员发现大多数情况下虽然加了锁,但是没有竞争的发生,甚至是同一个线程反复获得这个锁。那么偏向锁就为了针对这种情况。
举个例子,一个仓库管理员管着钥匙,然而每一次都是老王去借,仓库管理员于是就认识了老王,直接和他说,“行,你直接拿就是不用填表格了我记得你”。
讲一下偏向锁的具体过程。首先JVM要设置为可用偏向锁。然后当一个进程访问同步块并且获得锁的时候,会在对象头和栈帧的锁记录里面存储取得偏向锁的线程ID。
下一次有线程尝试获取锁的时候,首先检查这个对象头的MarkWord是不是储存着这个线程的ID。如果是,那么直接进去而不需要任何别的操作。如果不是,那么分为两种情况。1,对象的偏向锁标志位为0(当前不是偏向锁),说明发生了竞争,已经膨胀为轻量级锁,这时使用CAS操作尝试获得锁(这个操作具体是轻量级锁的获得锁的过程下面讲)。2,偏向锁标志位为1,说明还是偏向锁不过请求的线程不是原来那个了。这时只需要使用CAS尝试把对象头偏向锁从原来那个线程指向目前求锁的线程。这种情况举个例子就是老王准备退休了,他儿子接替他来拿钥匙,于是仓库管理员认识了他儿子,他儿子每次来也不用登记注册了。
这个CAS失败了呢?首先必须明确这个CAS为什么会失败,也就是说发生了竞争,有别的线程和它抢锁并且抢赢了,那么这个情况下,它就会要求撤销偏向锁(因为发生了竞争)。接着它首先暂停拥有偏向锁的线程,检查这个线程是否是个活动线程,如果不是,那么好,你拿了锁但是没在干事,锁还记录着你,那么直接把对象头设置为无锁状态重新来过。如果还是活动线程,先遍历栈帧里面的锁记录,让这个偏向锁变为无锁状态,然后恢复线程。
再说轻量级锁。这是偏向锁膨胀之后的产物。
加锁的过程:JVM在当前线程的栈帧中创建用于储存锁记录的空间(LockRecord),然后把MarkWord放进去,同时生成一个叫Owner的指针指向那个加锁的对象,同时用CAS尝试把对象头的MarkWord成一个指向锁记录的指针。至于synchronized的原理 也是阿里问的热点问题
成功了就拿到了锁。那么失败了呢?失败了的说法比较多。主流有《深入理解JVM》的说法和《并发编程的艺术》的说法。
《深入理解JVM》的说法:
失败了,去查看MarkWord的值。有2种可能:1,指向当前线程的指针,2,别的值。
如果是1,那么说明发生了“重入”的情况,直接当做成功获得锁处理。
其实这个有个疑问,为什么获得锁成功了而CAS失败了,这里其实要牵扯到CAS的具体过程:先比较某个值是不是预测的值,是的话就动用原子操作交换(或赋值),否则不操作直接返回失败。在用CAS的时候期待的值是其原本的MarkWord。发生“重入”的时候会发现其值不是期待的原本的MarkWord,而是一个指针,所以当然就返回失败,但是如果这个指针指向这个线程,那么说明其实已经获得了锁,不过是再进入一次。如果不是这个线程,那么情况2:
如果是2,那么发生了竞争,锁会膨胀为一个重量级锁(MutexLock)
《并发编程的艺术》的说法:
失败了直接自旋。期望在自旋的时间内获得锁,如果还是不能获得,那么开始膨胀,修改锁的MarkWord改为重量级锁的指针,并且阻塞自己。
解锁过程:(那个拿到锁的线程)用CAS把MarkWord换回到原来的对象头,如果成功,那么没有竞争发生,解锁完成。如果失败,表示存在竞争(之前有线程试图通过CAS修改MarkWord),这时要释放锁并且唤醒阻塞的线程。
JAVA锁的膨胀过程和优化(阿里)的更多相关文章
- JAVA锁的膨胀过程和优化
首先说一下锁的优化策略. 1,自旋锁 自选锁其实就是在拿锁时发现已经有线程拿了锁,自己如果去拿会阻塞自己,这个时候会选择进行一次忙循环尝试.也就是不停循环看是否能等到上个线程自己释放锁.这个问题是基于 ...
- java并发笔记之四synchronized 锁的膨胀过程(锁的升级过程)深入剖析
警告⚠️:本文耗时很长,先做好心理准备,建议PC端浏览器浏览效果更佳. 本篇我们讲通过大量实例代码及hotspot源码分析偏向锁(批量重偏向.批量撤销).轻量级锁.重量级锁及锁的膨胀过程(也就是锁的升 ...
- synchronized(三) 锁的膨胀过程(锁的升级过程)深入剖析
警告⚠️:本文耗时很长,先做好心理准备................哈哈哈 本篇我们讲通过大量实例代码及hotspot源码分析偏向锁(批量重偏向.批量撤销).轻量级锁.重量级锁及锁的膨胀过程(也就是 ...
- JAVA锁的优化和膨胀过程
转自:https://www.cnblogs.com/dsj2016/p/5714921.html https://cloud.tencent.com/developer/article/103675 ...
- java架构之路(多线程)synchronized详解以及锁的膨胀升级过程
上几次博客,我们把volatile基本都说完了,剩下的还有我们的synchronized,还有我们的AQS,这次博客我来说一下synchronized的使用和原理. synchronized是jvm内 ...
- Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)
Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...
- Java 锁优化
一.重量级锁 Java中,Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的.但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的.而操作系统实现 ...
- synchronized的实现原理——锁膨胀过程
@ 目录 前言 正文 偏向锁 轻量锁 批量重偏向 批量撤销 重量锁 总结 前言 上一篇分析了优化后的synchronized在不同场景下对象头中的表现形式,还记得那个结论吗?当一个线程第一次获取锁后再 ...
- 【转】Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)
一.重量级锁 上篇文章中向大家介绍了Synchronized的用法及其实现的原理.现在我们应该知道,Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的.但是监视器锁本 ...
随机推荐
- 2019-11-29-win10-uwp-颜色转换
原文:2019-11-29-win10-uwp-颜色转换 title author date CreateTime categories win10 uwp 颜色转换 lindexi 2019-11- ...
- C#4.0的十种语法糖
https://www.cnblogs.com/dotnet261010/p/6055092.html
- 一款神器,批量手机号码归属地查询软件,支持导出Excel表格
很多人查询手机号码归属地,还在一个一个百度去查太慢了,如果有几万个那么是不是要百度很久 有一款软件很多人都不知道,可以吧号码复制进去,即使里面有汉字也可以把手机号码挑出来,然后查询归属地,还具有号码去 ...
- Eureka重要对象简介
在进行分析EurekaClient和EurekaServer之间通信的源码之前,我们首先需要熟悉一下几个实体类 InstanceInfo 这个类代表着EurekaClient实例,客户端向服务端请求注 ...
- Spring所有注解大揭秘
声明bean的注解 @Component 组件,没有明确的角色 @Service 在业务逻辑层使用(service层) @Repository 在数据访问层使用(dao层) @Controller 在 ...
- vue单页面应用中动态修改title
https://www.jianshu.com/p/b980725b62e8 https://www.npmjs.com/package/vue-wechat-title 详细信息查看:vue-wea ...
- 英语dialogite红纹石dialogite菱锰矿
红纹石又称菱锰矿(dialogite)属于三方晶系.晶体呈菱面体,通常呈粒状.块状或结核状.玫瑰色,容易氧化而转变成褐黑色.玻璃光泽.解理平行菱面体{1011}完全.硬度3.5-4.5.比重3.6-3 ...
- webview学习
Android中WebView使用6,js调java实现播放视频 https://blog.csdn.net/zhaihaohao1/article/details/77993890 android ...
- HubSpot company数据在UI上的展示和通过API方式进行获取
在网页查看所有的company: https://app.hubspot.com/contacts/6798828/companies/list/view/all/? 打开第一个名为SAP的compa ...
- python使用sched模块执行周期性任务和定时任务
执行周期性任务 sched模块是一个通用的事件调度程序,可以对任务进行延迟调度,基于此,可以用它来实现周期性任务. # coding:utf8 import time import sched # 初 ...