再有人说synchronized是重量级锁,就把这篇文章扔给他看
synchronized作为Java程序员最常用同步工具,很多人却对它的用法和实现原理一知半解,以至于还有不少人认为synchronized是重量级锁,性能较差,尽量少用。
但不可否认的是synchronized依然是并发首选工具,连volatile、CAS、ReentrantLock都无法动摇synchronized的地位。synchronized是工作面试中的必备技能,今天就跟着一灯一块深入剖析synchronized底层到底做了哪些优化?
synchronized是用来加锁的,而锁是加在对象上面,所以需要先聊一下JVM中对象构成。 1. 对象的构成
Java对象在JVM内存中由三块区域组成:对象头、实例数据和对齐填充。
对象头又分为: Mark Word(标记字段)、Class Pointer(类型指针) 、 数组长度 (如果是数组)。
实例数据 是对象实际有效信息,包括本类信息和父类信息等。
对齐填充 没有特殊含义,由于虚拟机要求 对象起始地址必须是8字节的整数倍 ,作用仅是字节对齐。
Class Pointer 是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
重点关注一下对象头中 Mark Word ,里面存储了对象的hashcode、锁状态标识、持有锁的线程id、GC分代年龄等。
在32为的虚拟机中,Mark Word的组成如下:
2. synchronized锁优化
从JDK1.6开始,就对synchronized的实现机制进行了较大调整,包括使用JDK1.5引进的CAS自旋之外,还增加了自适应的CAS自旋、锁消除、锁粗化、偏向锁、轻量级锁等优化策略。由于使得synchronized性能极大提高,同时语义清晰、操作简单、无需手动关闭,所以推荐在允许的情况下尽量使用此关键字,同时在性能上此关键字还有优化的空间。
锁主要存在四种状态,依次是: 无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态 ,性能依次是从高到低。锁可以从偏向锁升级到轻量级锁,再升级的重量级锁。 但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级 。
在 JDK 1.6 中默认是开启偏向锁和轻量级锁的 ,可以通过-XX:-UseBiasedLocking来禁用偏向锁。 2.1 自旋锁
线程的挂起与恢复需要CPU从用户态转为内核态,频繁的阻塞和唤醒对CPU来说是一件负担很重的工作,势必会给系统的并发性能带来很大的压力。同时我们发现在许多应用上面, 对象锁的锁状态只会持续很短一段时间,为了这一段很短的时间频繁地阻塞和唤醒线程是非常不值得的 。
自旋锁就是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。
自旋锁适用于锁保护的临界区很小的情况,临界区很小的话,锁占用的时间就很短。自旋等待不能替代阻塞,虽然它可以避免线程切换带来的开销,但是它占用了CPU处理器的时间。如果持有锁的线程很快就释放了锁,那么自旋的效率就非常好,反之,自旋的线程就会白白消耗掉处理的资源,它不会做任何有意义的工作,这样反而会带来性能上的浪费。所以说,自旋等待的时间(自旋的次数)必须要有一个限度,如果自旋超过了定义的时间仍然没有获取到锁,则应该被挂起。
自旋锁在JDK 1.4.2中引入,默认关闭,但是可以使用-XX:+UseSpinning开开启,在JDK1.6中默认开启。同时自旋的默认次数为10次,可以通过参数-XX:PreBlockSpin来调整。 2.2 自适应自旋锁
JDK 1.6引入了更加智能的自旋锁,即 自适应自旋锁 。 自适应就意味着自旋的次数不再是固定的,它是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定 。那它如何进行适应性自旋呢?
线程如果自旋成功了,那么下次自旋的次数会更加多 ,因为虚拟机认为既然上次成功了,那么此次自旋也很有可能会再次成功,那么它就会允许自旋等待持续的次数更多。反之, 如果对于某个锁,很少有自旋能够成功 ,那么在以后要或者这个锁的时候自旋的次数会减少甚至省略掉自旋过程,以免浪费CPU资源。
有了自适应自旋锁,随着程序运行和性能监控信息的不断完善,虚拟机对程序锁的状况预测会越来越准确,虚拟机会变得越来越聪明。 2.3 锁消除
JVM在JIT编译时通过对运行上下文的扫描,经过逃逸分析,对于某段代码不存在竞争或共享的可能性,就会讲这段代码的 锁消除 ,提升程序运行效率。 public void method() { final Object LOCK = new Object(); synchronized (LOCK) { // do something } }
比如上面代码中锁,是方法中私有的,又是不可变的,完全没必要加锁,所以JVM就会执行 锁消除 。 2.4 锁粗化
按理来说,同步块的作用范围应该尽可能小,仅在共享数据的实际作用域中才进行同步,这样做的目的是为了使需要同步的操作数量尽可能缩小,缩短阻塞时间,如果存在锁竞争,那么等待锁的线程也能尽快拿到锁。
但是加锁解锁也需要消耗资源,如果存在一系列的连续加锁解锁操作,可能会导致不必要的性能损耗。
锁粗化就是将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁,避免频繁地加锁解锁操作。 public void method(Object LOCK) { synchronized (LOCK) { // do something1 } synchronized (LOCK) { // do something2 } }
比如上面方法中两个加锁的代码块,完全可以合并成一个,减少频繁加锁解锁带来的开销,提升程序运行效率。 2.5 偏向锁
为什么要引入偏向锁?
因为经过HotSpot的作者大量的研究发现,大多数时候是不存在锁竞争的,通常是一个线程多次获得同一把锁,因此如果每次都要竞争锁会增大很多没有必要付出的代价,为了降低获取锁的代价,才引入的偏向锁。 2.6 轻量级锁
轻量级锁考虑的是竞争锁对象的线程不多,而且线程持有锁的时间也不长的场景。因为阻塞线程需要CPU从用户态转到内核态,代价较大,如果刚刚阻塞不久这个锁就被释放了,那这个代价就有点得不偿失了,因此这个时候就干脆不阻塞这个线程,让它自旋(CAS)这等待锁释放。 加锁过程: 当代码进入同步块时,如果同步对象为无锁状态时,当前线程会在栈帧中创建一个锁记录( Lock Record )区域,同时将锁对象的对象头中 Mark Word 拷贝到锁记录中,再尝试使用 CAS 将 Mark Word 更新为指向锁记录的指针。如果更新成功,当前线程就获得了锁。
解锁过程: 轻量锁的解锁过程也是利用 CAS 来实现的,会尝试锁记录替换回锁对象的 Mark Word 。如果替换成功则说明整个同步操作完成,失败则说明有其他线程尝试获取锁,这时就会唤醒被挂起的线程(此时已经膨胀为重量锁 )2.7 重量级锁
synchronized是通过对象内部的监视器锁(Monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的互斥锁(Mutex Lock)来实现的。
重量级锁的工作流程:当系统检查到锁是重量级锁之后,会把等待想要获得锁的线程进行阻塞,被阻塞的线程不会消耗cpu。但是阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态转换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长,所以重量级锁的开销还是很大的。
在锁竞争激烈、锁持有时间长的场景,还是适合使用重量级锁的。 2.8 锁升级过程
2.9 锁的优缺点对比
锁的性能从低到高,依次是无锁、偏向锁、轻量级锁、重量级锁。不同的锁只是适合不同的场景,大家可以依据实际场景自行选择。
3. 总结
synchronized锁经过多次迭代优化,已经不像以前那么重了,在JDK1.8的ConcurrentHashMap源码中已经大量使用synchronized做同步控制,大家在日常开发中可以放心使用了。
金哨马宁被曝出吹假球或被陈戌源牵连随着李铁和陈戌源的落马,让本就对中国足球失去信心的球迷再一次蒙上一层阴影。而李铁涉及的假球事件也直接让这次扫黑风暴牵扯到了裁判身上。纵观国内联赛争议判罚连连,其中不乏有国内知名裁判
蔡振华被传卷入陈戌源案,名记李璇辟谣,建议球迷有证据直接举报最近陈戌源作为足协主席被查之后,看到和他相关的人员都接连成为大家怀疑和质疑的对象,在这其中成为众矢之的,或者受到攻击比较大的就是前足协主席蔡振华。蔡振华虽然现在已经全身而退,退休多
狗哥贝弗利的励志经历值得学习,放弃勇士加盟公牛继续追梦NB联赛作为全世界顶级的篮球赛事,不管是从商业模式或是球员实力方面都是最出色的。有很多天赋一般的球员,凭借着个人努力,最终进入NBA联盟并且还站稳了脚跟。原本勇士可以提升后场防守,
艾灸错穴位会有什么后果?错误艾灸反伤身很多艾人们常常会问,艾灸是不是把不舒服的地方灸热就好了,灸错穴位会有什么影响吗?可以明确的说,如果没有掌握正确的艾灸方式可能会导致灸疗效果事半功半,甚至反而会伤身!今
宁津县中医院公益中医养生课堂走进老年大学2月20日下午,宁津县中医院中药师刘庆贺来到宁津县老年大学,为老同学们带来一堂科普养生与健康膳食的课程,全程干货满满,受到了学员们的热烈欢迎。刘庆贺以中医经典黄帝内经中的内容入手,
智能化数字化激发中医药高质量发展新动能光明网讯(记者赵金悦)走进位于广东省佛山市南海区,国药集团中药控股的中药配方颗粒企业,广东一方制药有限公司标本中心内,一排排药物标本映入眼帘。这里展示的是从全国24个省区采集回来的
中医治疗冠心病(胸痹)经方1则,温阳化饮,益气活血方药炙甘草桂枝太子参五味子丹参瓜蒌薤白当归川芎益母草细辛菟丝子怀牛膝茯苓泽泻车前子汉防己。主治冠心病(胸痹)。治则温阳化饮,益气活血。用法每日1剂,水煎服,分早晚饭后两小时温服。方
小儿蛋白尿伴有浮肿乏力,中医经典药方1剂,健脾补肺,益气固表蛋白尿是各种原发性或继发性肾小球疾病的主要临床表现之一,不但短期内难以控制且易反复,即使一般症状好转消失,尿蛋白也可长时间存在。部分患者隐匿起病,仅以蛋白尿为早期表现,发病之初无其
捡花记梁灵芝侍弄花草时,我眼前常浮现曾经的小院,朴素的砖瓦房前,花草静默。大门两旁,栀子芍药月季争奇斗艳西墙头,喇叭花和路人打招呼墙根儿下,木槿两株白的黄的菊几丛。那时,日子散淡,我和花
美翻了我的村安徽省黄山市休宁县蓝田镇培育文明乡风谱写幸福新篇开局之年看美丽乡村,为生动展现全国文明村镇风采,中国文明网开展美翻了我的村第四季网上征集展示活动,邀您一起记录展示新时代新征程全面推进乡村振兴的生动实践和丰硕成果。安徽省黄山市休宁
焦作不只有郭朝辉张琳,更是人杰地灵的福地焦作市位于河南省西北部,北依太行,南临黄河,面积4071平方公里,人口373万。焦作高新区是焦作市辖功能区,成立于1999年,和2013年经河南省委省政府批准设立的城乡一体化示范区