前言
锁消除与锁粗化是虚拟机针对低效的锁操作而进行的一个优化。
锁消除
简单来说锁消除是虚拟机根据一个对象是否真正存在同步情况,若不存在同步情况,则对该对象的访问无需经过加锁解锁的操作。
你可能会有疑问,对象有没有同步我还不知道吗?有同步我就用synchronize,没有就不用了。但是实际情况是,有许多同步措施并不是程序员自己加入的。看下面的例子。
1 | public class Test { |
通过调用createStringBuffer
方法,将两个字符串拼接起来,然后返回。了解过StringBuffer
的同学一定清楚它是线程安全的,从append
方法就可以知道:
1 |
|
上面的代码是不存在锁的竞争的,那么如果在append的时候还需要去判断对象是否被占用,这部分的性能消耗是无意义的,因为根本不存在同步情况。于是虚拟机在即时编译的时候就会将上面代码进行优化,也就是锁消除。那么虚拟机什么时候才会使用锁消除呢?通过逃逸分析。那么什么是逃逸分析呢?还是看上面的代码:
1 | public static String createStringBuffer(String str1, String str2) { |
首先 sBuf 对象使用的范围仅仅只在这个方法栈中,因为return回去的对象是一个新的String对象。
1 |
|
也就是说 sBuf 对象是不会逃逸出去从而被其他线程访问到,那就可以把它们当做栈上的数据对待,认为它们是线程私有,同步锁无须进行。关于逃逸分析还不清楚的,可以看这篇博客。
注意锁消除的前提是:要运行在server模式下,且开启了逃逸分析。
锁粗化
锁粗化比较好理解,如下面的代码:
1 | public static StringBuffer createStringBuffer(String str1, String str2) { |
当频繁对 sBuf 进行加锁、解锁,会造成性能上的损失。如果虚拟机探测到有一串零碎操作都是对同一对象加锁,将会把加锁同步的范围扩展到整个操作序列的外部,也就是在第一个和最后一个append操作之后。