redis实现分布式锁

多个进程通过使用setnx命令(也就是setIfAbsent方法)向redis中插入同一条key-value键值对

这句话是核心

1.什么是分布式锁

同步锁和分布式锁的区别:

同步锁:一台机器里的多个线程同时访问

分布式锁:多台机器即多个客户端(一个进程代表一台机器)多个进程同时访问,任何时刻只有一个客户端能持有锁

总结:同步是多个线程,分布式锁是多个进程

2.最简单的一个redis分布式锁

redis的setnx命令 :set  if not  exists,即当指定的key不存在时,为key设置指定的值,此时setnx命令其实相当于set命令,当指定的key存在时,什么也不做

 代码实现:

多个进程通过使用setnx命令(也就是setIfAbsent方法)向redis中插入同一条key-value键值对插入成功的进程成功加锁,返回true,没有插入成功就没有成功加锁,返回false,待业务代码执行完毕,加锁成功的进程再删除这条key-value键值对就是解锁。

如下面的代码所示:成功执行这条setnx命令的客户端,result就不为空,可以执行业务代码,没有成功执行这条setnx命令的,result就为空,不能执行业务代码

然后删除这条刚刚插入的key-value(这就表示释放锁了),这个时候其他客户端可以执行成功这条setnx插入语句,这样result就不为空,也可以执行业务代码了)

//通过使用setnx命令(也就是setIfAbsent方法)向redis中插入一条key-value键值对的方式拿到锁
Boolean  result=stringRedisTemplate.opsForValue().setIfAbsent("job","programmer");
//result不为空,说明这个客户端拿到锁
//result为空,证明这个客户端没有拿到锁

if(result==false)//没有拿到锁
{
   return “error”;
}

//从redis中取出stock这个key的value,这时拿到的是字符串,将其转化为整数
int  stock=Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));

if(stock>0)//如果此时库存大于0,就扣减库存,stock-1
{
     
   stringTemplate.opsForValue("stock",stock-1+"”);
   System.out.println("扣减库存成功");
}
else
{
       System.out.println("库存不足,扣减失败");
}


stringRedisTemplate.delete("job");//释放锁,就是删除刚刚插入的key-value键值对

3.改进,防止死锁

上面的方式有可能会造成死锁,比如成功拿到锁的客户端扣减库存的逻辑抛异常了,那就不会执行到释放锁的逻辑,那么该锁是一直没有释放,其他请求时一直无法再成功加锁的,会成为死锁的,其他请求无法再扣减该商品

所以需要给这个插入的键值对设置自动过期时间(这里设置了过期时间为10s,足够执行业代码)

Boolean  result=stringRedisTemplate.opsForValue().setIfAbsent("job","programmer");
stringRedisTemplate.expire("job",10,TimeUnit.SECONDS);//设置过期时间为10s

 或者直接这么写:两行代码合并

Boolean result=stringRedisTemplate.opsForValue().setIfAbsent("job","programmer",10,TimesUnit.SECONDS);

4.线程A加锁,这把锁却被线程B释放掉了,我们希望别的线程不能释放掉我加的锁

线程1加锁成功,并且设置过期时间为5秒,然后线程开始执行业务逻辑

如果线程1执行时间超过5秒,还没执行完业务逻辑锁的到期时间已经到了

此时线程2成功加上锁了,也设置过期时间,然后一直在执行自己的业务逻辑

此时线程1执行完了业务逻辑,于是去释放锁,此时释放的是线程2加的锁,而不是线程1自己加的锁

解决方法:多个客户端插入的key-value只有key相同,value不同,设置成key=欲扣减库存的商品id,value=线程id

比如说:

线程1设置的是10000011:00012

线程2设置的是10000011:00013

注意setnx命令只要插入的这个key-value键值对key已经存在就会返回false,而不需要key-value完全一样

然后在线程释放锁的时候,先比较一下要删除的key-value的value是不是和自己的线程id一样,如果不一样是不可以删除这个key-value键值对的

使用Redisson实现分布式锁

这个更简单了,只需要短短几行代码Lock()和UnLock()

引入相关依赖包: 

@Autowired  
private  Redisson  redisson;

RLock lock=redisson.getLock(“job”);



lock.tryLock(30,TimesUnit.SECONDS);


int  stock=Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));

if(stock>0)//如果此时库存大于0,就扣减库存,stock-1
{
     
   stringTemplate.opsForValue("stock",stock-1+"”);
   System.out.println("扣减库存成功");
}
else
{
       System.out.println("库存不足,扣减失败");
}


lock.unlock();

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值