synchronized 优化手段之锁膨胀机制! (2)

对象在内存中的布局如下:

image.png


在 JDK 1.6 中默认是开启偏向锁的,可以通过“-XX:-UseBiasedLocking=false”命令来禁用偏向锁。

2.轻量级锁

引入轻量级锁的目的是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统 Mutex Lock(互斥锁)产生的性能消耗。如果使用 Mutex Lock 每次获取锁和释放锁的操作都会带来用户态和内核态的切换,这样系统的性能开销是很大的。

当关闭偏向锁或者多个线程竞争偏向锁时就会导致偏向锁升级为轻量级锁,轻量级锁的获取和释放都通过 CAS 完成的,其中锁获取可能会通过一定次数的自旋来完成。

注意事项

需要强调一点:轻量级锁并不是用来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用产生的性能消耗。轻量级锁所适应的场景是线程交替执行同步块的情况,如果同一时间多个线程同时访问时,就会导致轻量级锁膨胀为重量级锁。

3.重量级锁

synchronized 是依赖监视器 Monitor 实现方法同步或代码块同步的,代码块同步使用的是 monitorenter 和 monitorexit 指令来实现的,monitorenter 指令是在编译后插入到同步代码块的开始位置,而 monitorexit 是插入到方法结束处和异常处的,任何对象都有一个 Monitor 与之关联,当且一个 Monitor 被持有后,它将处于锁定状态。

如以下加锁代码:

public class SynchronizedToMonitorExample { public static void main(String[] args) { int count = 0; synchronized (SynchronizedToMonitorExample.class) { for (int i = 0; i < 10; i++) { count++; } } System.out.println(count); } }

当我们将上述代码编译成字节码之后,它的内容是这样的:

image.png


从上述结果可以看出,在 main 方法的执行中多个 monitorenter 和 monitorexit 的指令,由此可知 synchronized 是依赖 Monitor 监视器锁实现的,而监视器锁又是依赖操作系统的互斥锁(Mutex Lock),互斥锁在每次获取和释放锁时,都会带来用户态和内核态的切换,这样就增加了系统的性能开销。

总结

synchronized 在 JDK 1.6 时优化了其性能,在一系列优化的手段中,锁膨胀是提升 synchronized 执行效率的关键手段之一,锁膨胀指的是 synchronized 会从无锁状态、到偏向锁、到轻量级锁,最后到重量级锁的过程。重量级之前的所有状态在绝大数情况下可以大幅的提升 synchronized 的性能。

本系列推荐文章

并发第一课:Thread 详解

Java中用户线程和守护线程区别这么大?

深入理解线程池 ThreadPool

线程池的7种创建方式,强烈推荐你用它...

池化技术到达有多牛?看了线程和线程池的对比吓我一跳!

并发中的线程同步与锁

synchronized 加锁 this 和 class 的区别!

volatile 和 synchronized 的区别

轻量级锁一定比重量级锁快吗?

这样终止线程,竟然会导致服务宕机?

SimpleDateFormat线程不安全的5种解决方案!

ThreadLocal不好用?那是你没用对!

ThreadLocal内存溢出代码演示和原因分析!

Semaphore自白:限流器用我就对了!

CountDownLatch:别浪,等人齐再团!

CyclicBarrier:人齐了,司机就可以发车了!

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwxxgs.html