高并发场景下怎么优化加锁方式

技术高并发场景下怎么优化加锁方式本篇内容介绍了“高并发场景下怎么优化加锁方式”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成

本文介绍了“如何在高并发场景中优化锁定模式”的知识。很多人在实际案例的操作中会遇到这样的困难。让边肖带领你学习如何处理这些情况。希望大家认真阅读,学点东西!

例如,在完成转账操作的过程中,我们一次申请账户A和账户B,然后在两个账户申请成功后进行转账操作。其中,在我们实现的转移方法中,我们使用了一个无限循环来循环获取资源,直到账户A和账户B同时被获取。核心代码如下所示。

//申请转出账户和转入账户一次,直到成功的同时(!请求者。applyresources (this,target)){//循环体为空;}如果在很短的时间内执行ResourcesRequester类的applyResources()方法,并发程序引起的冲突很少,程序循环几次到几十次就可以同时获得转出账户和转入账户,这个方案是可行的。

但是,如果ResourcesRequester类的applyResources()方法执行时间较长,或者并发程序导致的冲突比较大,可能需要几千个周期才能同时获取转出账户和转入账户。这消耗了太多的CPU资源。这个时候,这个方案是不可行的。

那么,有没有办法优化这个方案呢?

问题分析

既然一直用无限循环获取资源的方案存在问题,那我们换个思路想吧。当线程执行时,发现条件不满足。线程能否进入等待状态?当条件满足时,通知等待线程重新执行?

也就是说,如果线程要求的条件没有满足,我们就让线程进入等待状态;如果满足线程要求的条件,我们将通知等待的线程重新执行。这样可以避免程序循环等待进而消耗CPU的问题。

然后,问题又来了!不满足条件时,如何让线程等待?当条件满足时,如何唤醒线程?

是的,这是一个问题!但是这个问题解决起来也很简单。简单来说,就是使用线程的等待和通知机制。

线程的等待与通知机制

我们可以利用线程的等待和通知机制来优化阻塞请求和持有条件时循环获取账户资源的问题。具体的等待和通知机制如下。

执行线程首先获取互斥体,如果线程继续执行时不满足要求的条件,则释放互斥体并进入等待状态。当满足线程继续执行所需的条件时,等待的线程会得到通知,互斥体会被重新获取。

那么,说了这么多,Java支持这种线程等待和通知机制吗?其实这个问题有点废话。这么优秀(牛逼)的语言肯定支持Java,实现起来也相对简单。

Java实现线程的等待与通知机制

实现方式

实际上,用Java语言实现线程等待和通知机制的方法有很多。这里我简单列举一种方式。别人可以自己想,自己体会。如果你什么都不懂,也可以问我!

在Java语言中,实现线程等待和通知机制的一种简单方法是使用synchronized,并结合wait(),notifyAll()和notifyAll()方法。

实现原理

当我们使用同步锁定时,只允许一个线程进入同步保护的代码块,也就是临界区。如果一个线程进入临界区,其他线程将在阻塞队列中等待,阻塞队列与同步互斥体是一对一的关系,也就是说,互斥体对应一个独立的阻塞队列。

在并发编程中,如果一个线程获得了同步互斥,但不满足继续向下执行的条件,则需要进入等待状态。此时可以用Java中的wait()方法来实现。调用wait()方法时,当前线程将被阻塞,并将进入等待队列等待。通过调用wait()方法进入的等待队列也是互斥体的等待队列。此外,当一个线程进入等待队列时,它会释放已经获取的互斥体,这样其他线程就有机会获取互斥体,然后进入关键区域。整个过程可以表示如下图所示。

当满足线程执行的条件时,可以使用Java提供的notify()和notifyAll()方法通知互斥等待队列中的线程。我们可以简单地用下图来展示这个过程。

在这里,你要注意以下事项:

(1)用notify()和notifyAll()方法通知线程时,调用notify()和notifyAll()方法时,满足线程的执行条件,但当线程实际执行时,条件可能不再满足,其他线程可能已经进入执行的临界区。

(2)当被通知的线程继续执行时,需要先获取互斥体,因为调用wait()方法等待时互斥体已经被释放。

(wait()、notify()和notifyAll()方法操作的队列是互斥的等待队列。如果此对象被同步锁定,则必须使用this.wait()、this.notify()和this.notifyAll()方法。如果targ上有同步锁,

et对象,则一定要使用target.wait()、target.notify()和target.notifyAll()方法。

(4)wait()、notify()和notifyAll()方法调用的前提是已经获取了相应的互斥锁,也就是说,wait()、notify()和notifyAll()方法都是在synchronized方法中或代码块中调用的。如果在synchronized方法外或代码块外调用了三个方法,或者锁定的对象是this,使用target对象调用三个方法的话,JVM会抛出java.lang.IllegalMonitorStateException异常。

具体实现

实现逻辑

在实现之前,我们还需要考虑以下几个问题:

  • 选择哪个互斥锁

在之前的程序中,我们在TansferAccount类中,存在一个ResourcesRequester  类的单例对象,所以,我们是可以使用this作为互斥锁的。这一点大家需要重点理解。

  • 线程执行转账操作的条件

转出账户和转入账户都没有被分配过。

  • 线程什么时候进入等待状态

线程继续执行需要的条件不满足的时候,进入等待状态。

  • 什么时候通知等待的线程执行

当存在线程释放账户的资源时,通知等待的线程继续执行。

综上,我们可以得出以下核心代码。

while(不满足条件){     wait(); }

那么,问题来了!为何是在while循环中调用wait()方法呢?因为当wait()方法返回时,有可能线程执行的条件已经改变,也就是说,之前条件是满足的,但是现在已经不满足了,所以要重新检验条件是否满足。

实现代码

我们优化后的ResourcesRequester类的代码如下所示。

public class ResourcesRequester{     //存放申请资源的集合     private List<Object> resources = new ArrayList<Object>();     //一次申请所有的资源     public synchronized void applyResources(Object source, Object target){         while(resources.contains(source) || resources.contains(target)){             try{                 wait();             }catch(Exception e){                 e.printStackTrace();             }         }         resources.add(source);         resources.add(targer);     }          //释放资源     public synchronized void releaseResources(Object source, Object target){         resources.remove(source);         resources.remove(target);         notifyAll();     } }

生成ResourcesRequester单例对象的Holder类ResourcesRequesterHolder的代码如下所示。

public class ResourcesRequesterHolder{     private ResourcesRequesterHolder(){}          public static ResourcesRequester getInstance(){         return Singleton.INSTANCE.getInstance();     }     private enum Singleton{         INSTANCE;         private ResourcesRequester singleton;         Singleton(){             singleton = new ResourcesRequester();         }         public ResourcesRequester getInstance(){             return singleton;         }     } }

执行转账操作的类的代码如下所示。

public class TansferAccount{     //账户的余额     private Integer balance;     //ResourcesRequester类的单例对象     private ResourcesRequester requester;         public TansferAccount(Integer balance){         this.balance = balance;         this.requester = ResourcesRequesterHolder.getInstance();     }     //转账操作     public void transfer(TansferAccount target, Integer transferMoney){         //一次申请转出账户和转入账户,直到成功         requester.applyResources(this, target))         try{             //对转出账户加锁             synchronized(this){                 //对转入账户加锁                 synchronized(target){                     if(this.balance >= transferMoney){                         this.balance -= transferMoney;                         target.balance += transferMoney;                     }                    }             }         }finally{             //最后释放账户资源             requester.releaseResources(this, target);         }     } }

可以看到,我们在程序中通知处于等待状态的线程时,使用的是notifyAll()方法而不是notify()方法。那notify()方法和notifyAll()方法两者有什么区别呢?

notify()和notifyAll()的区别

  • notify()方法

随机通知等待队列中的一个线程。

  • notifyAll()方法

通知等待队列中的所有线程。

在实际工作过程中,如果没有特殊的要求,尽量使用notifyAll()方法。因为使用notify()方法是有风险的,可能会导致某些线程永久不会被通知到!

“高并发场景下怎么优化加锁方式”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/49658.html

(0)

相关推荐

  • 真丝裙,真丝裙贱上水就有痕迹怎么办

    技术真丝裙,真丝裙贱上水就有痕迹怎么办真丝绸的一个特性就是局部沾水后容易形成水渍痕迹。这是真丝纤维本身亲水性太强、局部在沾水后真丝裙,纤维大分子沾到水的部位,水分子跟纤维上的亲水性基团比如羟基(-OH)、氨基(-NH)发

    生活 2021年10月30日
  • Java访问权限原理与用法分析

    技术Java访问权限原理与用法分析这篇文章主要介绍“Java访问权限原理与用法分析”,在日常操作中,相信很多人在Java访问权限原理与用法分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”

    攻略 2021年11月4日
  • 互联网中好用简洁的项目管理软件有哪些

    技术互联网中好用简洁的项目管理软件有哪些这篇文章主要介绍互联网中好用简洁的项目管理软件有哪些,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!TeamLabTeamLab 是一个免费开源的商业协作和项

    攻略 2021年11月2日
  • asp.net core 中Service层的实现样板是怎样的

    技术asp.net core 中Service层的实现样板是怎样的这期内容当中小编将会给大家带来有关asp.net core 中Service层的实现样板是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文

    攻略 2021年11月15日
  • 首字下沉,word中怎样设置首字下沉

    技术首字下沉,word中怎样设置首字下沉原发布者:悲回风之摇蕙兮 怎么设置首字下沉Word首字下沉设置方法发布时间:2012-09-2712:19 作者:本站整理 来源: 509次阅读Word软件是我们最常用的办公软件,

    生活 2021年10月19日
  • poj 1111,注意临时变量的选取不要重复)

    技术poj 1111,注意临时变量的选取不要重复) poj 1111(注意临时变量的选取不要重复)#includeiostream
    #includequeue
    #includecstring
    using

    礼包 2021年11月26日