作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

synchronized 在 JDK 1.5 时性能是比较低的,然而在后续的版本中经过各种优化迭代,它的性能也得到了前所未有的提升,上一篇中我们谈到了锁膨胀对 synchronized 性能的提升,然而它也只是“众多” synchronized 性能优化方案中的一种,那么我们本文就来盘点一下 synchronized 的核心优化方案。

synchronized 核心优化方案主要包含以下 4 个:

  1. 锁膨胀

  2. 锁消除

  3. 锁粗化

  4. 自适应自旋锁

1.锁膨胀

我们先来回顾一下锁膨胀对 synchronized 性能的影响,所谓的锁膨胀是指 synchronized 从无锁升级到偏向锁,再到轻量级锁,最后到重量级锁的过程,它叫做锁膨胀也叫做锁升级。JDK 1.6 之前,synchronized 是重量级锁,也就是说 synchronized 在释放和获取锁时都会从用户态转换成内核态,而转换的效率是比较低的。但有了锁膨胀机制之后,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();
}

以上代码经过编译之后的字节码如下:

从上述结果可以看出,之前我们写的线程安全的加锁的 StringBuffer 对象,在生成字节码之后就被替换成了不加锁不安全的 StringBuilder 对象了,原因是 StringBuffer 的变量属于一个局部变量,并且不会从该方法中逃逸出去,所以此时我们就可以使用锁消除(不加锁)来加速程序的运行。

3.锁粗化

锁粗化是指,将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁

我只听说锁“细化”可以提高程序的执行效率,也就是将锁的范围尽可能缩小,这样在锁竞争时,等待获取锁的线程才能更早的获取锁,从而提高程序的运行效率,但锁粗化是如何提高性能的呢?

没错,锁细化的观点在大多数情况下都是成立了,但是一系列连续加锁和解锁的操作,也会导致不必要的性能开销,从而影响程序的执行效率,比如这段代码:

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

本系列原创文章推荐

1.线程的故事:我的3位母亲成就了优秀的我!

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

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

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

5.漫画:如何证明sleep不释放锁,而wait释放锁?

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

7.求求你,别再用wait和notify了!

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

9.CountDownLatch:别浪,等人齐再团!

10.CyclicBarrier:人齐了,老司机就发车了!

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

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

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

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

15.synchronized 加锁 this 和 class 的区别!

16.synchronized 优化手段之锁膨胀机制!

synchronized 中的 4 个优化,你知道几个?相关推荐

  1. 常见的锁策略、synchronized中的锁优化机制

    一.常见的锁策略 锁策略,和普通程序猿基本没啥关系,和 "实现锁" 的人才有关系的 这里所提到的锁策略,和 Java 本身没关系,适用于所有和 "锁" 相关的情 ...

  2. 数据仓库中的SQL性能优化 - Hive篇

    一个Hive查询生成多个map reduce job,一个map reduce job又有map,reduce,spill,shuffle,sort等多个阶段,所以针对hive查询的优化可以大致分为针 ...

  3. AI中pass架构设计优化

    AI中pass架构设计优化 Relay 和 TVM IR,包含一系列优化passes,可提高模型的性能指标,例如平均推理,内存占用,或特定设备的功耗.有一套标准优化,及特定机器学习的优化,包括常量折叠 ...

  4. DRO:SFM任务中的深度循环优化器(阿里巴巴AI Lab)

    代码.论文地址:在公众号「3D视觉工坊」,后台回复「DRO」,即可直接下载. Motivation: 解决一个优化问题,常见的优化器比如梯度下降法, 牛顿法等, 一般会先计算梯度------>再 ...

  5. 计算机算法对程序设计的作用,计算机编程中数学算法的优化策略

    李钰 摘要:在计算机编程中,合理地运用数学算法所拥有的优势不但可以完好地针对所拥有的问题进行总结分类归纳,还可以将其归纳作为基础从而进行针对性的统一计算,并且能够将非常复杂的问题进行整体的简化并且将其 ...

  6. 为什么wait和notify只能在synchronized中?

    前言 wait和notify必须在synchronized块中,否则会抛出IllegalMonitorStateException. 原因 代码示例 class BlockingQueue {Queu ...

  7. 企业网络推广专员浅析企业网络推广中网站关键词排名优化有何技巧?

    自从企业网站上线运营之后,站长通过对企业网站自身定位规划的优化策略逐步展开优化进程,利用熟知的网站优化方法帮助企业网站快速获得搜索引擎收录,有了收录更能为用户呈现出企业网站优质的一面,获得良好排名助力 ...

  8. 在网络推广外包中企业网站排名优化在网络推广外包中如何实现?

    随着互联网市场中企业网站数量的增加,网站优化人才的需要与日俱增,据资料显示,与国内当下亿万网民相比,我国目前仅有不足100万家企业从事网络营销,对于网络营销人才的需求也是巨大,未来几年将产生数以百万计 ...

  9. Mysql中查询速度的优化

    2019独角兽企业重金招聘Python工程师标准>>> mysql中查询速度的优化 1.查看每一个表的数据量 2.left join的时候尽量减少表的大小,通过临时表,查询条件去限制 ...

最新文章

  1. (C++)用指针实现两数交换函数swap()的两种方法
  2. python爬虫学习(一)
  3. oracle创建用户需要什么权限,Oracle 创建普通用户,并赋予权限
  4. 我常用的python函数(part1)--内置函数isinstance
  5. Hibernate查询缓存如何工作
  6. 使用JUnit的ExpectedException和@Rule测试自定义异常
  7. java简单ai聊天_一个hello/hi的简单的网络聊天程序——JAVA
  8. 4.19计算机网络笔记
  9. 《程序员修炼之道》读后感02
  10. 编译openjdk时cygwin需要下载的内容
  11. Jmeter性能测试之命令行执行和生成测试报告
  12. Center Loss层
  13. 基于Selenium和ChromeDriver的自动化页面性能测试
  14. java 移动目录_java 移动文件夹内的文件,从一个目录移动到另外一个目录
  15. shadow acne(阴影失真)和peter panning(阴影悬浮)
  16. Python 爬取外文网站并翻译中文和百度搜索验证
  17. 小米Note标配版 双网通刷原生安卓7.0教程(详细)
  18. 获取Windows系统密码凭证 (゚益゚メ) 渗透测试
  19. 5.20,福利一波,用文字拼一个自己最爱的人
  20. 5G+物联网商业模式促使物联网卡迎来增长新风口

热门文章

  1. java匿名对象 回收_Java 匿名对象
  2. libzdb 连接mysql,数据库连接池库libzdb使用教程
  3. 非关型数据库之Hbase
  4. Shell Pyramid【数学+二分】
  5. 【python】 time模块和datetime模块详解 【转】
  6. Linux Shell获取系统资源使用百分比(CentOS)
  7. Python模拟删除字符串两边的空白
  8. ln命令总结,软链接硬链接文件删除原理画图详解
  9. python写一段脚本代码自动完成输入(目录下的所有)文件的数据替换(修改数据和替换数据都是输入的)【转】...
  10. .NET APlayer播放器 demo