锁消除

举报
上善若水. 发表于 2022/09/11 19:45:53 2022/09/11
【摘要】 自旋锁在JDK 1.6中默认开启。自旋等待不能代替阻塞,且先不说对处理器数量的要求,自旋等待本身虽然避免了线程切换的开销,但它是要占用处理器时间的,因此如果锁被占用的时间很短,自旋等待的效果就会非常好,反之,如果锁被占用的时间很长,那么自旋的线程会白白浪费处理器资源,反而带来性能上的浪费。因此,自旋等待的时间必须有一定的限度,如果自旋超过了限定的次数仍然没有成功获取锁,就应该使用传统的方式来...

自旋锁在JDK 1.6中默认开启。自旋等待不能代替阻塞,且先不说对处理器数量的要求,自旋等待本身虽然避免了线程切换的开销,但它是要占用处理器时间的,因此如果锁被占用的时间很短,自旋等待的效果就会非常好,反之,如果锁被占用的时间很长,那么自旋的线程会白白浪费处理器资源,反而带来性能上的浪费。因此,自旋等待的时间必须有一定的限度,如果自旋超过了限定的次数仍然没有成功获取锁,就应该使用传统的方式来挂起线程了。

在JDK 1.6中引入了自适应的自旋锁,自适应意味着自选的时间不再固定了,而是由上一次在同一个锁上自旋时间以及锁的拥有者的状态来决定。

锁消除
锁消除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。

例如,如下代码(看起来没有同步的代码)
public String concatString(String s1,String s2,String s3){
return s1+s2+s3;
}
我们知道,由于String是一个不可变的类,对字符串的连接操作总是通过生成新的String对象来进行的,因此Javac编译器会对String连接做自动优化。在JDK 1.5之后,会转化成StringBuffer对象的连续append()操作。

public String concatString(String s1,String s2,String s3){ 
	StringBuffer sb=new StringBuffer();
	sb.append(s1);
	sb.append(s2);
	sb.append(s3);
	return sb.toString();
}

现在大家还认为这段代码没有同步吗?每个StringBuffer.append()方法都有一个同步块,锁就是sb对象。虚拟机观察变量sb,很快就会发现它的动态作用域限制在concatString()方法内部,其他线程无法访问它,因此虽然这里有锁,但是可以被完全的消除掉,在 即时编译后,这段代码就会忽略掉所有的同步而直接执行了。

锁粗化
原则上,我们在编写代码的时候,总是推荐将同步块的作用范围限制得尽量小——只在共享数据的实际作用域中才进行同步,这样是为了使得需要同步的操作数量尽可能变小,如果存在锁竞争,那么等待锁的线程也能尽快拿到锁。

大部分情况下,上面的原则是对的,但是如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作时出现在循环体中,那及时没有锁竞争,频繁的进行互斥同步操作也会导致不必要的性能损耗。

连续的StringBuffer.append()方法就属于这类情况,如果虚拟机探测到有这样一串零碎的操作都对同一个对象加锁,将会把加锁同步不范围(粗化)到整个操作序列的外部(例如,第一个append()操作之前知道最后一个appen()操作之后,这样只需要加锁一次就可以了)。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。