当前位置: 首页 > 科技观察

请问Synchronized中的四个优化,你知道几个?

时间:2023-03-20 21:19:27 科技观察

作者|王磊来源|Java中文社区(ID:javacn666)转载请联系授权(微信ID:GG_Stone)。synchronized在JDK1.5中的性能比较低,但在后续版本经过各种优化迭代后,其性能也得到了前所未有的提升。在上一篇文章中,我们谈到了通过锁扩展来提升同步性能。然而,它只是“众多”同步性能优化方案中的一种。所以本文我们就来盘点一下synchronized。核心优化程序。synchronized核心优化方案主要包括以下四种:锁扩展锁消除锁粗化自适应自旋锁1.锁扩展我们先回顾一下锁扩展对synchronized性能的影响。所谓锁扩容是指synchronized从lockless升级到偏向锁,再到轻量级锁,最后到重量级锁的过程称为锁扩容或锁升级。在JDK1.6之前,synchronized是一个重量级的锁,也就是说synchronized在释放和获取锁的时候会从用户态转换到内核态,但是转换效率比较低。但是有了锁扩展机制,synchronized状态多了无锁、偏向锁、轻量级锁。此时,在进行并发操作时,大部分场景都不需要从用户态到内核态的转换。这大大提高了同步的性能。》PS:至于为什么不需要用户态到内核态的转换呢?请移步锁扩展的文章:《synchronized 优化手段之锁膨胀机制》。2.锁的消除。很多人都理解synchronized中锁扩展的机制,但是对于next3如果你对这三个优化不是很了解的话,面试的时候会错过一个很好的机会,所以这篇文章就分别来说说这三个优化。无法检测到某段代码被共享竞争的可能性,就会消除这段代码所属的同步锁,从而提高程序的性能。消除锁的基础是逃逸分析的数据支持,比如StringBuffer的append()方法,或者Vector的add()方法,很多时候都可以进行消除锁,比如下面的代码:publicStringmethod(){StringBuffersb=newStringBuffer();for(inti=0;i<10;i++){sb.append("i:"+i);}returnsb.toString();}上面代码编译后的字节码如下:from从上面的结果可以看出,我们之前写的线程安全的带锁的StringBuffer对象在字节码生成后被换成了一个未锁的不安全的StringBuilder对象。原因是StringBuffer的变量属于局部变量,不会从这个方法中逃逸,所以这时候我们可以使用锁消除(无锁)来加快程序的运行速度。3.锁粗化锁粗化是指将多个连续的加锁和解锁操作连接在一起,扩展成一个范围更大的锁。只听说锁“细化”可以提高程序的执行效率,也就是尽可能缩小锁的范围,这样在锁竞争的时候,等待获取锁的线程可以获取到更早加锁,从而提高程序的运行效率,但是锁粗化是如何提高性能的呢?是的,锁细化的视图在大多数情况下是成立的,但是一系列连续的加锁和解锁操作也会导致不必要的性能开销。从而影响程序的执行效率,比如这段代码:publicStringmethod(){StringBuildersb=newStringBuilder();for(inti=0;i<10;i++){//伪代码:锁操作sb.append("i:"+i);//伪代码:解锁操作}returnsb.toString();}这里不考虑编译器的优化。如果在for循环中定义锁,锁的作用范围很小,但是每次for循环都需要加锁和释放锁,性能很低;但是如果我们直接在for循环的外层加一个锁,那么这段代码对于同一个对象的性能会提升很多,如下代码所示:publicStringmethod(){StringBuildersb=newStringBuilder();//伪代码:锁操作for(inti=0;i<10;i++){sb.append("i:"+i);}//伪代码:解锁操作returnsb.toString();}锁粗化的效果:如果检测到同一个对象连续进行了加锁和解锁操作,这一系列的操作会合并成一个更大的锁,从而提高程序的执行效率。4.自适应自旋锁自旋锁是指一种通过自身循环尝试获取锁的方式。伪代码实现如下://Trytoacquirealockwhile(!isLock()){}自旋锁的好处是避免了某些线程的suspend和resume操作,因为suspend线程和resume线程需要从用户态转移到内核态,这个过程比较慢,所以通过自旋可以在一定程度上避免线程suspend和resume操作。性能开销。但是如果自旋时间长了还不能获取到锁,也会造成一定的资源浪费,所以我们通常给自旋设置一个固定的值,避免一直自旋带来的性能开销。不过,对于synchronized关键字来说,它的自旋锁就更“聪明”了。synchronized中的自旋锁是自适应自旋锁,就像之前开的手动挡三轮车,但是经过JDK1.6优化后,我们的“车”一下子变成了自动挡的兰博基尼。自适应自旋锁是指线程自旋的次数不再是一个固定值,而是一个动态变化的值。这个值会根据上一次自旋获取锁的状态来决定本次的自旋次数。比如上次通过自旋成功获取到锁,那么这次也有可能通过自旋获取到锁,所以这次自旋的次数会增加,如果上次没有通过自旋成功获取到锁,那么本次自旋可能无法获取到锁,所以为了避免资源浪费,会少一些或者不用循环来提高程序的执行效率。简单来说,如果线程自旋成功,则下一次自旋的次数会增加,如果失败,则下一次自旋的次数会减少。总结在本文中,我们介绍了synchronized的四种优化方案,其中锁扩展和自适应自旋锁是synchronized关键字本身的优化实现,而锁消除和锁粗化则是JVM虚拟机为synchronized提供的优化方案。这些优化方案最终大大提升了synchronized的性能,也让它在并发编程中占有一席之地。【编辑推荐】HarmonyOS官方战略合作共建——HarmonyOS技术社区云端容器:你有哪些选择?5G消息全面进入发展期什么是域名劫持?如何应对域名劫持自学编程,首先应该选择什么语言?一篇文章读懂网络爬虫发展史