基于 Redis 实现分布式锁思考

推荐大家关注一个公众号

点击上方 "编程技术圈"关注, 星标或置顶一起成长

后台回复“大礼包”有惊喜礼包!

日英文

Two things always to be remembered in life- don't take any decisions when you are angry & dont make any promises when you are happy! 

生活中一定要记住这两件事:不在生气时做决定,不在高兴时轻许诺言。

每日掏心话

随欲而安的缈茫,虚空的一如既往。人生在世,面对无数的诱惑和磨难,往往不得不在舍与得之间彷徨徘徊。
责编:乐乐 | 来自:溪~源链接:blog.csdn.net/xuan_lu/article/details/111600302

编程技术圈(ID:study_tech)第 1302次推文

往日回顾:腾讯员工吐槽:团队来了个阿里高p,瞬间会议变多,群多了

     

   正文   

分布式锁基于redis实现分布式锁思考几个问题???
synchronized锁为什么不能应用于分布式锁?synchronized虽然能够解决同步问题,但是每次只有一个线程访问,并且synchronized锁属于JVM锁,仅适用于单点部署;然而分布式需要部署多台实例,属于不同的JVM线程对象
使用redis中setnx实现分布式锁。//设置分布式锁
String lockKey = "product_001_key";
//语义:如何不存在则存入缓存中,且返回true;
//否则已存在,则返回false即加锁失败
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "product_001_lock");
if (!result) {
  //没有加锁成功,则返回提示等
}
try{
  
}catch() {
  
}finally{
  //释放锁
  stringRedisTemplate.delete(lockKey);
}
针对以上设置分布式锁思考一下问题?
1.如果突然服务器宕机,那么必然造成锁无法释放,即造成死锁?解决方案:设置超时时间。
//设置分布式锁
String lockKey = "product_001_key";
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "product_001_lock");
//设置锁超时时间30s
stringRedisTemplate.expire(lockKey,30, TimeUnit.SECONDS);
if (!result) {
  //没有加锁成功,则返回提示等
}
try{
  
}catch() {
  
}finally{
  //释放锁
  stringRedisTemplate.delete(lockKey);
}
2.加锁和设置超时时间中间引起服务器宕机,则一样会导致死锁。Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "product_001_lock");
//------服务器宕机,则超时时间未设置成功-------
//设置锁超时时间30s
stringRedisTemplate.expire(lockKey,30, TimeUnit.SECONDS);
解决方案:原子性操作,即同时加锁和设置超时时间;
即上面的代码合并成一句操作:
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"product_001_lock", 30, TimeUnit.SECONDS)
3.思考超时时间设置是否合理呢?即线程执行时间和锁超时时间并非一致。场景:假设设置加锁超时时间10s;
高并发场景下,线程A执行时间为15s,redis依据超时时间,将其线程A加的锁释放掉;然后线程B获取锁,并加锁成功,此时线程A执行结束,执行finally代码块就会将线程B加的锁释放。
解决方案:设置线程随机ID,释放锁时判断是否为当前线程加的锁,即使存在线程A因线程执行时间超时被动释放其锁,但至少保证当前超时线程不会释放其他线程加的锁。但是面对线程执行时间大于设置的超时时间,也是会存在并发问题。
搜索公众号顶级架构师后台回复“offer”,获取一份惊喜礼包。
String lockKey = "product_001";
String clientId = UUID.randomUUID().toString();
//设置超时时间,且加锁和设置线程ID
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,clientId, 30, TimeUnit.SECONDS)`
  
if (!result) {
  //没有加锁成功,则返回提示等
}
try{
  
}catch() {
  
}finally{
  //释放锁:加锁线程ID和当前执行线程ID相同,才允许释放锁
 if (clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){
        stringRedisTemplate.delete(lockKey);
    }
} 
4.上面场景解决方案:加锁续命即续线程锁超时时间解决方案:加锁成功时,开启一个后台线程,每隔10s(自定义)判断当前线程是否还持有锁,持有锁则再续命30s等
Redission实现分布式锁
实现原理流程:
   String lockKey = "product_001";
        //获取锁对象,并未加锁
        RLock redissonLock = redisson.getLock(lockKey);
        try {
            // **此时加锁**,实现锁续命功能
            redissonLock.lock();
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); 
            if (stock > 0) {
                int realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock", realStock + ""); 
                System.out.println("扣减成功,剩余库存:" + realStock + "");
            } else {
                System.out.println("扣减失败,库存不足");
            }
        }finally {
          //释放锁
            redissonLock.unlock();
        }
总结综上,设计实现分布式锁需要满足一下条件:
互斥性;在任意时刻,只有一个客户端能持有锁。
不能发生死锁;即使存在一个线程持有锁的期间崩溃而没有主动解锁,也能保证后续其他线程能加锁。
加锁和解锁必须是同一个线程。
你还有什么想要补充的吗?
PS:欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,欢迎转发分享给更多人。

版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢!

欢迎加入后端架构师交流群,在后台回复“学习”即可。

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。在这里,我为大家准备了一份2021年最新最全BAT等大厂Java面试经验总结。
别找了,想获取史上最简单的Java大厂面试题学习资料
扫下方二维码回复「面试」就好了


猜你还想看
阿里、腾讯、百度、华为、京东最新面试题汇集
消息幂等(去重)通用解决方案,真顶!

没想到《天龙八部》这段,只有搞IT的才懂

一架无人机就恢复河南灾区50km²通信!“翼龙”成功助小镇通网5小时

嘿,你在看吗?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值