锁的优化及注意事项

N!CE波 N!CE波     2022-08-31     782

关键词:

 
“锁”的竞争必然会导致程序的整体性能下降,以下就是为了降低这种辐作用的建议:
 
    1.减小锁持有时间
    如果线程持有锁的时间很长,那么相对地,锁的竞争程度也就越激烈。程序开发应该尽可能地减少对某个锁的占有时间,以减少线程间互斥的可能.
    public synchronized void syncMethod(){
    othercode1();
    mutextMethod();
    othercode2();}
   优化后:
    public void syncMethod2(){
    othercode1();
    synchronized(this){
        mutextMethod();
    }
    othercode2();}
 
    注意:减少锁的持有时间有助于降低锁冲突的可能性,进而提升系统的并发能力。
 
    2.减小锁粒度
    减小锁粒度也是一种削弱多线程锁竞争的有效手段。这种技术典型的使用场景就是Concurren-tHashMap类的实现。
    
ConcurrentHashMap实现原理:
    对于HashMap来说,最重要的两个方法就是get()和put()。一种最自然的想法就是对整个HashMap加锁,必然可以得到一个线程安全的对象。但是这样做,我们就认为加锁粒度太大。对于ConcurrentHashMap,它内部进一步细分了若干个小的HashMap,称之为段(SEGMENT)。默认情况下,一个ConcurrentHashMap被进一步细分为16个段。
    如果需要在ConcurrentHashMap中增加一个新的表项,并不是将整个HashMap加锁,而是首先根据hashcode得到该表项应该被存放到哪个段中,然后对该段加锁,并完成put()操作。在多线程环境中,如果多个线程同时进行put()操作,只要被加入的表项不存放在同一个段中,则线程间便可以做到真正的并行。由于默认有16个段,因此,如果够幸运的话,ConcurrentHashMap可以同时接受16个线程同时插入(如果都插入不同的段中),从而大大提供其吞吐量。
    根据key,获得对应的段的序号。接着得到段,然后将数据插入给定的段中。
 
    但是,减少锁粒度会引入一个新的问题,即:当系统需要取得全局锁时,其消耗的资源会比较多。
    比如ConcurrentHashMap的size()方法,要获取这个信息需要取得所有子段的锁,因此,只有在类似于size()获取全局信息的方法调用并不频繁时,这种减小锁粒度的方法才能真正意义上提高系统吞吐量。
 
注意:所谓减少锁粒度,就是指缩小锁定对象的范围,从而减少锁冲突的可能性,进而提高系统的并发能力。
    
    3.读写分离锁来替换独占锁
    使用读写锁ReadWriteLock可以提高系统的性能。使用读写分离锁来替代独占锁是减小锁粒度的一种特殊情况。如果减少锁粒度是通过分割数据结构实现的,那么,读写锁则是对系统功能点的分割。
    在读多写少的场合,读写锁对系统性能是很有好处的。因为如果系统在读写数据时均只使用独占锁,那么读操作和写操作间、读操作和读操作间、写操作和写操作间均不能做到真正的并发,并且需要相互等待。而读操作本身不会影响数据的完整性和一致性。因此,理论上讲,在大部分情况下,应该可以允许多线程同时读,读写锁正是实现了这种功能。
 
    注意:在读多写少的场合,使用读写锁可以有效提升系统的并发能力。
 
    4.锁分离
    如果将读写锁的思想做进一步的延伸,就是锁分离。读写锁根据读写操作功能上的不同,进行了有效的锁分离。依据应用程序的功能特点,使用类似的分离思想,也可以对独占锁进行分离。
    一个典型的案例就是java.util.concurrent.LinkedBlock-ingQueue的实现,采用两把不同的锁,分离了take()和put()操作。
 
    5.锁粗化
    通常情况下,为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽量短,即在使用完公共资源后,应该立即释放锁。只有这样,等待在这个锁上的其他线程才能尽早地获得资源执行任务。但是,凡事都有一个度,如果对同一个锁不停地进行请求、同步和释放,其本身也会消耗系统宝贵的资源,反而不利于性能的优化。
    为此,虚拟机在遇到一连串连续地对同一锁不断进行请求和释放的操作时,便会把所有的锁操作整合成对锁的一次请求,从而减少对锁的请求同步次数,这个操作叫做锁的粗化。
    
    那么在开发过程中,大家也应该有意识地在合理的场合进行锁的粗化,尤其当在循环内请求锁时。
    for(int i=0;i<CIRCLE;i++){
    synchronized(lock){
    } }
    优化后:
    synchronized(lock){
    for(int i=0;i<CIRCLE;i++){
    }}
 
    注意:性能优化就是根据运行时的真实情况对各个资源点进行权衡折中的过程。锁粗化的思想和减少锁持有时间是相反的,但在不同的场合,它们的效果并不相同。所以大家需要根据实际情况,进行权衡。
 
 
Java虚拟机对锁的优化
    1.锁偏向
    锁偏向是一种针对加锁操作的优化手段。它的核心思想是:如果一个线程获得了锁,那么锁就进入偏向模式。当这个线程再次请求锁时,无须再做任何同步操作。这样就节省了大量有关锁申请的操作,从而提高了程序性能。因此,对于几乎没有锁竞争的场合,偏向锁有比较好的优化效果,因为连续多次极有可能是同一个线程请求相同的锁。而对于锁竞争比较激烈的场合,其效果不佳。因为在竞争激烈的场合,最有可能的情况是每次都是不同的线程来请求相同的锁。这样偏向模式会失效,因此还不如不启用偏向锁。使用Java虚拟机参数-XX:+UseBiasedLocking可以开启偏向锁。
    
    2.轻量级锁
    如果偏向锁失败,虚拟机并不会立即挂起线程。它还会使用一种称为轻量级锁的优化手段。轻量级锁的操作也很轻便,它只是简单地将对象头部作为指针,指向持有锁的线程堆栈的内部,来判断一个线程是否持有对象锁。如果线程获得轻量级锁成功,则可以顺利进入临界区。如果轻量级锁加锁失败,则表示其他线程抢先争夺到了锁,那么当前线程的锁请求就会膨胀为重量级锁。
    
    3.自旋锁
    锁膨胀后,虚拟机为了避免线程真实地在操作系统层面挂起,虚拟机还会在做最后的努力——自旋锁。由于当前线程暂时无法获得锁,但是什么时候可以获得锁是一个未知数。也许在几个CPU时钟周期后,就可以得到锁。如果这样,简单粗暴地挂起线程可能是一种得不偿失的操作。因此,系统会进行一次赌注:它会假设在不久的将来,线程可以得到这把锁。因此,虚拟机会让当前线程做几个空循环(这也是自旋的含义),在经过若干次循环后,如果可以得到锁,那么就顺利进入临界区。如果还不能获得锁,才会真实地将线程在操作系统层面挂起。
    
    4.锁消除
    锁消除是一种更彻底的锁优化。Java虚拟机在JIT编译时,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁。通过锁消除,可以节省毫无意义的请求锁时间。
    我们必然会使用一些JDK的内置API,比如StringBuffer、Vector等。你在使用这些类的时候,也许根本不会考虑这些对象到底内部是如何实现的。比如,你很有可能在一个不可能存在并发竞争的场合使用Vector。而众所周知,Vector内部使用了synchronized请求锁。如果虚拟机检测到这种情况,就会将这些无用的锁操作去除。
 
    锁消除涉及的一项关键技术为逃逸分析。所谓逃逸分析就是观察某一个变量是否会逃出某一个作用域。
    比如:
    public String[] createStrings(){
    Vector<String> v=new Vector<String>();
    for(int i=0;i<100;i++){
        v.add(Integer.toString(i));
    } 
   return v.toArray(new String[]{});
    }
    如果creat-eStrings()返回的不是String数组,而是v本身,那么就认为变量v逃逸出了当前函数,也就是说v有可能被其他线程访问。如果是这样,虚拟机就不能消除v中的锁操作。
 
    逃逸分析必须在-server模式下进行,可以使用-XX:+DoEscapeAnalysis参数打开逃逸分析。使用-XX:+EliminateLocks参数可以打开锁消除。
 
 

4.1锁的优化及注意事项(代码片段)

一:减少锁的持有时间:  例如:  publicsynchronizedvoidall()aaa();bbb();ccc();如果就bbb方法存在并发情况,需要加锁,那么我们只需要给bbb方法添加同步方法即可:publicvoidall()aaa();synchronized(this)bbb();ccc();二:减小锁的粒度... 查看详情

锁的优化及注意事项(读书笔记)

有助于提高锁性能的几点建议减少锁的持有时间真正需要同步加锁的时候在加锁,减少锁的持有时间有助于减低锁冲突的可能性,进而提升系统的并发能力,减少颗粒度,所谓减少颗粒度就是指缩小锁定对象的范围,从而减少锁冲突的... 查看详情

锁的优化和注意事项

锁优化分为代码层面的优化和jvm层面的优化1.代码层面的锁优化的思路和方法一旦用到锁,就说明这是阻塞式的,所以在并发度上一般来说都会比无锁的情况低一点。这里提到的锁优化,是指在阻塞式的情况下,如何让性能不要... 查看详情

详解synchronized锁的各种用法及注意事项(代码片段)

...来阐述synchronized锁的各种用法以及使用synchronized锁的相关注意事项,记录下来同时也方便自己记忆。synchronized锁是jvm内置的锁,不同于Reentra 查看详情

详解synchronized锁的各种用法及注意事项(代码片段)

...来阐述synchronized锁的各种用法以及使用synchronized锁的相关注意事项,记录下来同时也方便自己记忆。synchronized锁是jvm内置的锁,不同于ReentrantLock锁。synchronized关键字可以修饰方法,也可以修饰代码块。synchronized关键... 查看详情

https配置优化及注意点(代码片段)

...参数比较少,但是可以根据实际情况对性能及安全性做成优化,具体如下!一、SSL参数具体优化(这里只填主要的)1.ssl_protocolsTLSv1.3TLSv1.2TLSv1.1TLSv1;苹果APP只支持TLSv1.2,会优先使用TLSv1.2,考虑到客户 查看详情

优化系列汇编优化技术:mips架构msaintrinsic优化及demo

...Arithmetic3.4、Saturate3.5、Shuffle3.6、Interleave3.7、Copy/Splat4、注意事项5、mipsmsaintrinsic优化demo6、龙芯3A4000优化建议7、更多资料1、简介</ 查看详情

优化系列汇编优化技术:mips架构msaintrinsic优化及demo

...Arithmetic3.4、Saturate3.5、Shuffle3.6、Interleave3.7、Copy/Splat4、注意事项5、mipsmsaintrinsic优化demo6、龙芯3A4000优化建议7、更多资料1、简介</ 查看详情

vsan的主机故障及优化

在上一篇文章中,我们已经知道了VSAN是如何处理容量设备和缓存设备磁盘故障的,那么,如果vsan主机发生故障,会如何呢?我们再来看看下面这幅图:这种情况与“磁盘故障”稍有不同。发生磁盘故障时,VSAN会注意到所发生的... 查看详情

synchronized原理及1.6之后的锁升级优化

...向锁。偏向第一个线程,这个线程在修改对象头成为偏向锁的时候使用CAS操作,并将对象头中的ThreadID改成自己的ID,之后再次访问这个对象时,只需要对比ID,不需要再使用CAS在进行操作。一旦有第二个线程访问这个对象,因为... 查看详情

优化系列汇编优化技术:arm架构32位汇编优化及demo

...总7、ARM32位加载数据的两种格式8、ARM32位汇编编写demo9、注意事项10、汇编优化基本准则11、ARM程序调试12、汇编优化demo1、前言前面的几篇文章详细讲解了X86架构纯汇编、 查看详情

优化读/写锁的实现

】优化读/写锁的实现【英文标题】:optimizetheimplementationofread/writelocks【发布时间】:2015-05-0504:35:48【问题描述】:我目前正在使用boost库实现读/写锁,而不使用shared_lock和unique_lock。我已经阅读了一些相关的问题(例如,Howwouldar... 查看详情

java并发编程常见锁及优化

...每次读写数据都会上锁进行阻塞2.自旋锁2.1原理如果持有锁的线程能在短时间内释放锁,等待锁的线程只需自旋(不需要进行用户线程和CPU内核切换,进入阻塞挂起而后唤醒[这里需要2个上下文切换过程])等锁释放后就能立... 查看详情

java多线程锁的优化策略

   锁的优化策略  编码过程中可采取的锁优化的思路有以下几种:  1:减少锁持有时间      例如:对一个方法加锁,不如对方法中需要同步的几行代码加锁;  2:减小锁粒... 查看详情

锁的优化(代码片段)

...不断的进行请求和释放的操作,把所有的锁操作整合成对锁的一次请求synchronized(lock)for(inti=0;i<20;i++)//dosth锁偏向如果一个线程获得锁,那么就进入偏向模式,下次再次请求锁时,无须进行同步操作,节省了锁申请的时间,对于... 查看详情

一句话以上题解及注意事项整理已完成的题目

jdfz.com1001A+B1006pla二维DP+单调队列优化1007美食家贪心策略,尽可能多选1008牛排序二分查找+贪心策略1009护花贪心策略,每次移动y/x最大的1010奶酪厂一维DP二维DP都可以做1043体育城带权并查集P.S.座位号可以重复1044span贪心可证,最... 查看详情

锁的优化

...锁本身不会带来多少性能的损耗,性能的损耗主要在获取锁的过程。1.尽量不要锁住方法,因为在普通成员函数上加锁,线程获得的是该方法所在对象的对象锁;2.缩小同步代码块,尤其是一些循环操作、同步IO操作,并且不止是... 查看详情

关于数据库锁的一些注意事项

insert、delete是会把整张表都锁住的,在事务没有提交之前,其他请求如果select这张表,将会被卡住,除非,你select的时候,是用主键进行查询的,如:select*fromtablenamewhereid=1,这样是不会被卡住(用sqlserver测试的)所以,任何sele... 查看详情