80%的人不知道的复述,分布式锁的正确实现方式(Java版)

  
  

本博客使用第三方开源组件能实现复述,客户端,且只考虑复述,服务端单机部署的场景。

     

前言

  

分布式锁一般有三种实现方式:1。数据库乐观锁;2。基于复述的分布式锁;3。基于管理员的分布式锁。本篇博客将介绍第二种方式,基于复述,实现分布式锁。虽然网上已经有各种介绍复述分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现复述分布式锁。

  

可靠性

  

首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

  
      <李>   

    <>强互斥性。在任意时刻,只有一个客户端能持有锁。

      李   <李>   

    <强>不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

      李   <李>   

    <强>具有容错性。只要大部分的复述,节点正常运行,客户端就可以加锁和解锁。

      李   <李> <>强解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。   
  

代码实现

  

组件依赖

  

首先我们要通过Maven引入<代码>能> pom.xml 文件加入下面的代码:

  
 <代码> & lt; dependency>
  & lt; groupId> redis.clients
  & lt; artifactId> jedis
  & lt; version> 2.9.0
  & lt;/dependency>  
  

加锁代码

  

正确姿势

  

说说很简单,给我代码。先展示代码,再带大家慢慢解释为什么这样实现:

  
 <代码>公共类RedisTool {
  
  私有静态最终字符串LOCK_SUCCESS=癘K”;
  私有静态最终字符串SET_IF_NOT_EXIST=癗X”;
  私有静态最终字符串SET_WITH_EXPIRE_TIME=癙X”;/* *
  *尝试获取分布式锁
  * @param能复述,客户端
  * @param lockKey锁
  * @param requestId请求标识
  * @param expireTime超期时间
  * @return是否获取成功
  */lockKey公共静态布尔tryGetDistributedLock(能能,字符串,字符串requestId, int expireTime) {=能字符串结果。集(lockKey、requestId SET_IF_NOT_EXIST、SET_WITH_EXPIRE_TIME expireTime);
  
  如果(LOCK_SUCCESS.equals(结果)){
  返回true;
  }
  返回错误;
  
  }
  
  } 
  

可以看的到,我们加锁就一行代码:<代码>能。集(nxxx字符串键,字符串值,字符串,字符串expx, int)>   

      <李>   

    第一个为关键,我们使用键来当锁,因为关键是唯一的。

      李   <李>   

    第二个为价值,我们传的是requestId,很多童鞋可能不明白,有关键作为锁不就够了吗,为什么还要用到价值?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件<强>解铃还须系铃人强,通过给值赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据.requestId可以使用<代码> UUID.randomUUID () .toString() 方法生成。

      李   <李>   

    第三个为nxxx,这个参数我们填的是NX,意思是如果不存在,即当关键不存在时,我们进行设置操作,若钥匙已经存在,则不做任何操作,

      李   <李>   

    第四个为expx,这个参数我们传的是PX,意思是我们要给这个键加一个过期的设置,具体时间由第五个参数决定。

      李   <李>第五个为一次,与第四个参数相呼应,代表关键的过期时间。   
  

总的来说,执行上面组的()方法就只会导致两种结果:1。当前没有锁(关键不存在),那么就进行加锁操作,并对锁设置个有效期,同时价值表示加锁的客户端。2。已有锁存在,不做任何操作。

  

心细的童鞋就会发现了,我们的加锁代码满足我们<强>可靠性里描述的三个条件。首先,设置()加入了NX参数,可以保证如果已有关键存在,则函数不会调用成功,也就是只有一个客户端能持有锁,满足互斥性。其次,由于我们对锁设置了过期时间,即使锁的持有者后续发生崩溃而没有解锁,锁也会因为到了过期时间而自动解锁(即键被删除),不会发生死锁。最后,因为我们将值赋值为requestId,代表加锁的客户端请求标识,那么在客户端在解锁的时候就可以进行校验是否是同一个客户端。由于我们只考虑复述,单机部署的场景,所以容错性我们暂不考虑。

80%的人不知道的复述,分布式锁的正确实现方式(Java版)