synchronized优化手段:锁膨胀、锁消除、锁粗化和自适应自旋锁... | 您所在的位置:网站首页 › sychronized优化 › synchronized优化手段:锁膨胀、锁消除、锁粗化和自适应自旋锁... |
synchronized 在 JDK 1.5 时性能是比较低的,然而在后续的版本中经过各种优化迭代,它的性能也得到了前所未有的提升,上一篇中我们谈到了锁膨胀对 synchronized 性能的提升,然而它也只是“众多” synchronized 性能优化方案中的一种,那么我们本文就来盘点一下 synchronized 的核心优化方案。 synchronized 核心优化方案主要包含以下 4 个: 锁膨胀 锁消除 锁粗化 自适应自旋锁 1.锁膨胀我们先来回顾一下锁膨胀对 synchronized 性能的影响,所谓的锁膨胀是指 synchronized 从无锁升级到偏向锁,再到轻量级锁,最后到重量级锁的过程,它叫做锁膨胀也叫做锁升级。
PS:至于为什么不需要用户态到内核态的转换?请移步到锁膨胀的那篇文章:《synchronized 优化手段之锁膨胀机制》。 2.锁消除很多人都了解 synchronized 中锁膨胀的机制,但对接下来的 3 项优化却知之甚少,这样会在面试中错失良机,那么我们本文就把这 3 项优化单独拎出来讲一下吧。 锁消除指的是在某些情况下,JVM 虚拟机如果检测不到某段代码被共享和竞争的可能性,就会将这段代码所属的同步锁消除掉,从而到底提高程序性能的目的。 锁消除的依据是逃逸分析的数据支持,如 StringBuffer 的 append() 方法,或 Vector 的 add() 方法,在很多情况下是可以进行锁消除的,比如以下这段代码: public String method() { StringBuffer sb = new StringBuffer(); for (int i = 0; i < 10; i++) { sb.append("i:" + i); } return sb.toString(); }以上代码经过编译之后的字节码如下:
锁粗化是指,将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁。 我只听说锁“细化”可以提高程序的执行效率,也就是将锁的范围尽可能缩小,这样在锁竞争时,等待获取锁的线程才能更早的获取锁,从而提高程序的运行效率,但锁粗化是如何提高性能的呢? 没错,锁细化的观点在大多数情况下都是成立了,但是一系列连续加锁和解锁的操作,也会导致不必要的性能开销,从而影响程序的执行效率,比如这段代码: public String method() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10; i++) { // 伪代码:加锁操作 sb.append("i:" + i); // 伪代码:解锁操作 } return sb.toString(); }这里我们不考虑编译器优化的情况,如果在 for 循环中定义锁,那么锁的范围很小,但每次 for 循环都需要进行加锁和释放锁的操作,性能是很低的;但如果我们直接在 for 循环的外层加一把锁,那么对于同一个对象操作这段代码的性能就会提高很多,如下伪代码所示: public String method() { StringBuilder sb = new StringBuilder(); // 伪代码:加锁操作 for (int i = 0; i < 10; i++) { sb.append("i:" + i); } // 伪代码:解锁操作 return sb.toString(); }锁粗化的作用:如果检测到同一个对象执行了连续的加锁和解锁的操作,则会将这一系列操作合并成一个更大的锁,从而提升程序的执行效率。 4.自适应自旋锁自旋锁是指通过自身循环,尝试获取锁的一种方式,伪代码实现如下: // 尝试获取锁 while(!isLock()){ }自旋锁优点在于它避免一些线程的挂起和恢复操作,因为挂起线程和恢复线程都需要从用户态转入内核态,这个过程是比较慢的,所以通过自旋的方式可以一定程度上避免线程挂起和恢复所造成的性能开销。 但是,如果长时间自旋还获取不到锁,那么也会造成一定的资源浪费,所以我们通常会给自旋设置一个固定的值来避免一直自旋的性能开销。然而对于 synchronized 关键字来说,它的自旋锁更加的“智能”,synchronized 中的自旋锁是自适应自旋锁,这就好比之前一直开的手动挡的三轮车,而经过了 JDK 1.6 的优化之后,我们的这部“车”,一下子变成自动挡的兰博基尼了。
本文我们介绍了 4 种优化 synchronized 的方案,其中锁膨胀和自适应自旋锁是 synchronized 关键字自身的优化实现,而锁消除和锁粗化是 JVM 虚拟机对 synchronized 提供的优化方案,这些优化方案最终使得 synchronized 的性能得到了大幅的提升,也让它在并发编程中占据了一席之地。 参考 & 鸣谢www.cnblogs.com/aspirant/p/11470858.html zhuanlan.zhihu.com/p/29866981 tech.meituan.com/2018/11/15/java-lock.html 本系列推荐文章 并发第一课:Thread 详解 Java中用户线程和守护线程区别这么大? 深入理解线程池 ThreadPool 线程池的7种创建方式,强烈推荐你用它... 池化技术到达有多牛?看了线程和线程池的对比吓我一跳! 并发中的线程同步与锁 synchronized 加锁 this 和 class 的区别! volatile 和 synchronized 的区别 轻量级锁一定比重量级锁快吗? 这样终止线程,竟然会导致服务宕机? SimpleDateFormat线程不安全的5种解决方案! ThreadLocal不好用?那是你没用对! ThreadLocal内存溢出代码演示和原因分析! Semaphore自白:限流器用我就对了! CountDownLatch:别浪,等人齐再团! CyclicBarrier:人齐了,司机就可以发车了! synchronized 优化手段之锁膨胀机制!关注公号「Java 中文社群」查看更多有意思、涨知识的 Java 并发文章。 |
CopyRight 2018-2019 实验室设备网 版权所有 |