jdk1.8对synchronized锁的优化

synchronized 锁的优化:锁的四种状态-无锁,偏向锁、轻量级锁,重量级锁

img

1、偏向锁:原因是大多数时候是不存在锁竞争的,常常是一个线程多次获得同一个锁,因此如果每次都要竞争锁会增大很多没有必要付出的代价,为了降低获取锁的代价,才引入的偏向锁

偏向锁的升级:当线程1访问代码块并获取锁对象时,会在java对象头和栈帧中记录偏向的锁的threadID,因为偏向锁不会主动释放锁,以后线程1再次获取锁时比较当前线程的threadID和Java对象头中的threadID是否一致,如果一致(还是线程1获取锁对象),则无需使用CAS来加锁、解锁;如果不一致(如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的threadID),那么查看Java对象头中记录线程1是否存活,如果没有存活,那么锁对象被重置为无锁状态,其它线程(线程2)可以竞争将其设置为偏向锁;如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。

 

2、轻量级锁:考虑的是竞争锁对象的线程不多,而且线程持有锁的时间也不长的情景。因为阻塞线程需要CPU从用户态转到内核态,代价较大,如果刚刚阻塞不久这个锁就被释放了,那这个代价就有点得不偿失了,因此这个时候就干脆不阻塞这个线程,让它自旋这等待锁释放。

轻量级锁什么时候升级为重量级锁?线程1获取轻量级锁时会先把锁对象的对象头MarkWord复制一份到线程1的栈帧中创建的用于存储锁记录的空间(称为DisplacedMarkWord),然后使用CAS把对象头中的内容替换为线程1存储的锁记录(DisplacedMarkWord)的地址;

如果在线程1复制对象头的同时(在线程1CAS之前),线程2也准备获取锁,复制了对象头到线程2的锁记录空间中,但是在线程2CAS的时候,发现线程1已经把对象头换了,线程2的CAS失败,那么线程2就尝试使用自旋锁来等待线程1释放锁。

但是如果自旋的时间太长也不行,因为自旋是要消耗CPU的,因此自旋的次数是有限制的,比如10次或者100次,如果自旋次数到了线程1还没有释放锁,或者线程1还在执行,线程2还在自旋等待,这时又有一个线程3过来竞争这个锁对象,那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞,防止CPU空转。

3、重量级锁:为了避免无用的自旋,轻量级锁一旦膨胀为重量级锁就不会再降级为轻量级锁了;偏向锁升级为轻量级锁也不能再降级为偏向锁。一句话就是锁可以升级不可以降级,但是偏向锁状态可以被重置为无锁状态。

4、锁粗化:同步块的作用范围应该尽可能小,仅在共享数据的实际作用域中才进行同步,这样做的目的是为了使需要同步的操作数量尽可能缩小,缩短阻塞时间,如果存在锁竞争,那么等待锁的线程也能尽快拿到锁。 但是加锁解锁也需要消耗资源,如果存在一系列的连续加锁解锁操作,可能会导致不必要的性能损耗。 锁粗化就是将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁,避免频繁的加锁解锁操作。

5、锁消除

Java虚拟机在JIT编译时(可以简单理解为当某段代码即将第一次被执行时进行编译,又称即时编译),通过对运行上下文的扫描,经过逃逸分析,去除不可能存在共享资源竞争的锁,通过这种方式消除没有必要的锁,可以节省毫无意义的请求锁时间

总结:轻量级锁,重量级锁是针对不同的场景的,偏向锁是针对不存在或很少存在资源竞争的情况,轻量级锁是存在线程竞争,执行周期比较短的情况,大部分都是这情况,最主要的原因是轻量级锁是为了避免重量级锁,产生操作系统层面的用户态和内核态切换带来的开销。轻量级锁自旋远比用户态/内核态系统调度带来的自旋消耗要小的多,但是自旋是有CPU消耗的,所以又不可以永久的进行自旋,因此升级为重量级锁,进行线程同步处理。

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

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

相关文章

03.校准时间

复制代码保存为vbs,js等文件的时候,报莫名其妙的错误,把文件的编码格式保存为ANSI , utf8不行;VBS校准系统时间在有线网络行,无线就不行,请修改!!已解决! - VBS求助&讨论 - 批处理之家 批处理_BAT_CMD_DOS_VBS_Per…

淘宝开发平台 java 调用实例

Java调用示例代码 更新日期:2016-02-06访问次数:53432 主要步骤 填充公共参数 填充业务参数 计算请求签名 发起API调用 获取API结果 示例代码 import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.…

LeetCode 293. Flip Game

原题链接在这里:https://leetcode.com/problems/flip-game/description/ 题目: You are playing the following Flip Game with your friend: Given a string that contains only these two characters: and -, you and your friend take turns to flip…

Redis缓存,你真的懂了吗

为什么要用缓存(缓存的优点、场景) (1)在项目中缓存是如何使用的? 结合你自己项目的业务来,你如果用了那恭喜你,你如果没用那不好意思,你硬加也得加一个场景吧! &…

Java sdk 调用淘宝开发平台

public static void main(String[] args) throws Exception { // TOP服务地址,正式环境需要设置为http://gw.api.taobao.com/router/rest String serverUrl “http://gw.api.tbsandbox.com/router/rest”; String appKey “test”; // 可替换为您的沙箱环境应用的…

编写一个函数func(),将此函数的输入参数(int型)逆序输出显示,如54321 – 12345,要求使用递归,并且函数体代码不超过8行...

public class Test{  //中间变量private String res "0";  //方法public int func(int i){if(i>0){int temp i%10;res resString.valueOf(temp);func(i/10);}return Integer.valueOf(res);}public static void main(String[] args){Test tnew Test();int a…

你会用Java实现两个大数相加吗

两个大数相加(Java)* 1、是整数;* 2、两个数无限大,long都装不下;* 3、不能用BigInteger;* 4、不能用任何包装类提供的运算方法;* 5、两个数都是以字符串的方式提供。 * 思路:* 字符串逐位相加,…

获取淘宝开发平台的sessionKey

淘宝API调用 申请 获取session key 在调用淘宝的API时,我们都会用到appkey,appsecret,appsession。 1、我们申请应用就会有appkey和appsecret了 2、正式环境下获取SessionKey 注意:web插件平台应用和web其它应用在正式环境下是同样的获取方法 1&…

PERL 实现微信登录

get 请求: https://login.weixin.qq.com/jslogin? appidwx782c26e4c19acffb &redirect_urihttps%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage &funnew &langzh_CN &_1455501911998参数: _ 1455501911998 appid wx782…

安装git客户端

https://blog.csdn.net/sinat_16998945/article/details/81062278 https://blog.csdn.net/qq_38225558/article/details/86220668

SQL规范

一、三范式 1、 参考网址: http://www.cnblogs.com/linjiqin/archive/2012/04/01/2428695.html (1).第一范式(确保每列保持原子性) (2).第二范式(确保表中的每列都和主键相关) (3).第三范式(确保每列都和主键列直接相关,而不是间接…

618小记

转载:https://www.zhihu.com/question/379688667/answer/1488971063 任何专业的开发者都应该明白,实现方法服从于功能需求,而功能需求体现在接口定义中。 所以,必须要先确定接口定义,然后才能开始实现,…

关于多条id相同,只取其中一条记录的sql语句

需要使用:分区函数用法(partition by 字段) select *,row_number() over(partition by item order by date ) as index from tab 分区索引 ------------------------------------------- SQL Server select * from (select * , row_number() over(partition by id …