优化点
- 锁膨胀
- 锁粗化
- 锁消除
详解
锁膨胀(锁升级)
- 在1.6之前synchronized在加锁的时候开销是很大的,因为需要从操作系统申请锁,需要在用户空间和系统空间来回切换
- 1.6之后做了优化加入了锁膨胀过程来降低加锁开销
当第一个来访问加锁代码块并且没有其他线程竞争的时候会加偏向锁实现方式就是CAS自旋,
当有另外线程访问加锁代码块的时候会CAS自旋竞争锁,此时膨胀为轻量级锁到此都还是用户空间锁,当有线程自旋超过十次(-XX:PreBlockSpin设置)或者自旋线程超过了CPU核数的一半的时候此时锁会再次膨胀,变为重量级锁,此时就要从操作系统处申请锁会发生用户空间和系统空间的切换开销,当偏向锁状态下有耗时过程操作时例如wait则会跳过轻量级锁直接膨胀到重量级锁。
锁粗化
- JVM会在编译的时候对代码上下文的进行分析,然后把一些不合理的代码进行优化,比如在同一个方法里,多次调用同一个同步的方法,将多次加锁和释放,优化成一次加锁和释放。锁粗化就是将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁
for(int i=0;i<10000;i++){synchronized(this){ }
会被粗化成:
synchronized(this){for(int i=0;i<10000;i++){ }
锁消除
- 如果JVM检测到某段代码不可能存在共享数据竞争,JVM会对这段代码的同步锁进行锁消除。
在动态编译同步块的时候,JIT编译器可以借助一种被称为逃逸分析(Escape Analysis)的技术来判断同步块所使用的锁对象是否只能够被一个线程访问而没有被发布到其他线程。如果同步块所使用的锁对象通过这种分析被证实只能够被一个线程访问,那么JIT编译器在编译这个同步块的时候就会取消对这部分代码的同步
public static void lockCancellation(){
Object obj = new Object();
synchronized (obj){
System.out.println("未发生逃逸,锁自动消除");
}
}