redis作为分布式锁的运用,网上有无数的案例,这里提供一个我自己设计的unlock解锁方案。

相对于加锁,解锁的过程相对简单,之前我项目里解锁就是直接delete lock_key,由于加锁的过程设置了超时时间,简单的delete lock_key显然有造成误删锁的风险(下面会具体介绍误删的原因)。本人梳理了下解锁的流程,设计了一个大部分情况下可以避免误删的解锁方案。

首先引用一个典型的加锁流程。利用的jedis客户端,其中setnx方法判断锁是否被其他线程占用,如果被占用的话也不是马上退出,而是比较占用的锁是否超时。这儿getSet方法用来避免多个线程同时删除过期锁,从而同时获取到锁的情况,保证只有一个线程能修改过期锁。这个过程网上有无数的案例,在此不作过多描述。下面分析该方法的unlock方法如何解锁。

/** 典型加锁流程* */public boolean acquireLock(String lock) {boolean success = false;Jedis jedis = pool.getResource();//过期时间3分钟long value = System.currentTimeMillis() + 3 * 60 * 1000 + 1;long acquired = jedis.setnx(lock, String.valueOf(value));//SETNX成功,则成功获取一个锁if (acquired == 1)success = true;//SETNX失败,说明锁仍然被其他对象保持,检查其是否已经超时else {long oldValue = Long.valueOf(jedis.get(lock));//超时if (oldValue < System.currentTimeMillis()) {String getValue = jedis.getSet(lock, String.valueOf(value));// 获取锁成功if (Long.valueOf(getValue) == oldValue)success = true;// 已被其他进程捷足先登了elsesuccess = false;}//未超时,则直接返回失败elsesuccess = false;}pool.returnResource(jedis);return success;}

关于unlock方法,如果直接删除lock_key,显然可能存在这样的错误:

当T1加锁成功,但执行过程被挂起,导致执行时间超过3分钟,另一个线程T2修改了T1的锁过期时间,此时T2加锁成功。当T1执行到解锁的流程,如果T1直接delete lock_key,删除的是T2修改过的lock_key,这时候T2未必执行完毕。如果再有T3过来,将直接加锁成功,导致T2和T3并发。

问题的关键是,T1解锁的时候,没有验证删除的是自己锁。为了让T1线程能“认识”自己加的锁,我们修改了lock方法,返回加锁的时间戳,用于unlock判断。加锁代码:

 /** 加锁成功返回时间戳,加锁失败返回空对象null* */public String lock(String lock) {Jedis jedis = pool.getResource();long value = System.currentTimeMillis() + 3 * 60 * 1000 + 1;String timeStamp = String.valueOf(value);long acquired = jedis.setnx(lock, timeStamp);//SETNX成功,则成功获取一个锁if (acquired == 1) {return timeStamp;} else {long oldValue = Long.valueOf(jedis.get(lock));//超时if (oldValue < System.currentTimeMillis()) {String getValue = jedis.getSet(lock, timeStamp);// 这种情况不能加锁if (Long.valueOf(getValue) != oldValue) {timeStamp = null;}} else {//锁未超时,也不能加锁timeStamp = null;}}pool.returnResource(jedis);//只有加锁成功情况,才返回了加锁的时间戳return timeStamp;}

lock方法可以返回时间戳,当调用unlock方法时,用这个时间戳作为验证的参数

/** 解锁要验证加锁返回的时间戳* */public void unlock(String lock, String timeStamp) {Jedis jedis = pool.getResource();String value = jedis.get(lock); .......................................(1)long expireTime = Long.parseLong(value);if (System.currentTimeMillis() > expireTime&& value.equals(timeStamp)) {jedis.del(lock);    ...............................................(2)}pool.returnResource(jedis);}

这儿解释一下delete lock_key的条件:

1、当锁未超时(小于currentTimeMillis)

2、当锁的value和timeStamp相等

为什么要验证锁未超时?因为如果锁已经超时,就算value和timeStamp相等,即线程自己加的锁,如果直接删除这个超时的锁,可能删除的是另一个线程的锁,具体过程像这样:

(1)T1线程查看锁是否是自己的timeStamp:

String value = jedis.get(lock);

if(value.equals(timeStamp)) ==> true

//判断timeStamp是不是和value相同,结果相同,因此T1线程正准备删除key

(2)这时候T2线程过来获取锁,由于T1的锁已经超时,T2直接加锁成功了,这时候lock的value其实不在是timeStamp

(3)T1执行到删除的命令,然后悲剧发生,T2的lock_key被删掉了。。。

整个错误的核心在于,unlock方法里面(1)、(2)两个步骤之间,不能有别的线程加锁。

按照这个思路,redis的watch似乎可以更简洁的处理lock和unlock的过程。但据说watch涉及到redis事务开销,很少有用watch实现分布锁。如果读者有更好的方式,欢迎向博主推荐

(个人原创,转载请注明出处)

redis分布式锁unlock方法相关推荐

  1. Redis分布式锁那点事

    锁超时问题 在redis分布式锁中,如果线程A加锁成功了,但是由于业务功能耗时时间很长,超过了设置的超时时间,这时候redis会自动释放线程A加的锁.通常我们加锁的目的是:为了防止访问临界资源时,出现 ...

  2. redis分布式锁 在集群模式下如何实现_收藏慢慢看系列:简洁实用的Redis分布式锁用法...

    在微服务中很多情况下需要使用到分布式锁功能,而目前比较常见的方案是通过Redis来实现分布式锁,网上关于分布式锁的实现方式有很多,早期主要是基于Redisson等客户端,但在Spring Boot2. ...

  3. 简单介绍redis分布式锁解决表单重复提交的问题

    在系统中,有些接口如果重复提交,可能会造成脏数据或者其他的严重的问题,所以我们一般会对与数据库有交互的接口进行重复处理.本文就详细的介绍一下redis分布式锁解决表单重复提交,感兴趣的可以了解一下 假 ...

  4. Redis 分布式锁没这么简单,网上大多数都有 bug

    Redis 分布式锁这个话题似乎烂大街了,不管你是面试还是工作,随处可见,「码哥」为啥还写? 因为看过很多文章没有将分布式锁的各种问题讲明白,所以准备写一篇,也当做自己的学习总结. 在进入正文之前,我 ...

  5. 这才叫细:带你深入理解Redis分布式锁

    什么是分布式锁 说到Redis,我们第一想到的功能就是可以缓存数据,除此之外,Redis因为单进程.性能高的特点,它还经常被用于做分布式锁. 锁我们都知道,在程序中的作用就是同步工具,保证共享资源在同 ...

  6. @scheduled cron启动后和每小时执行_小耶哥: 一个Redis分布式锁又要和小鑫同学扯半个小时!...

    1 Redis分布式锁 |1-1 定时任务重复执行-问题引入 最近小耶哥在做一个功能, 什么功能呢? 就是超时未支付的订单我们要定时关闭, 释放库存, 并且短信通知用户该订单因超时被取消了.由于小耶哥 ...

  7. 简洁实用的Redis分布式锁用法

    在微服务中很多情况下需要使用到分布式锁功能,而目前比较常见的方案是通过Redis来实现分布式锁,网上关于分布式锁的实现方式有很多,早期主要是基于Redisson等客户端,但在Spring Boot2. ...

  8. Redis分布式锁 Spring Schedule实现任务调度

    一看到标题就知道,这一篇博客又是总结分布式工作环境中集群产生的问题,个人觉得分布式没有那么难以理解,可能也是自己见识比较浅,对我来说,分布式只是一种后端业务演进时的一种工作方式,而真正实现这种工作方式 ...

  9. Redisson实现Redis分布式锁的N种姿势

    点击蓝色"程序猿DD"关注我哟 来源:阿飞的博客 前几天发的一篇文章<Redlock:Redis分布式锁最牛逼的实现>,引起了一些同学的讨论,也有一些同学提出了一些疑问 ...

最新文章

  1. linux 忘记密码(以centos6为例)
  2. 腾讯计费:亿万级大促活动自动化保障体系
  3. SAP CRM呼叫中心和Hybris Backoffice的轮询设计
  4. JAVA设计模式 - 创建型模式总结
  5. HTML 5适合小公司,适合在大平台上做内容
  6. 85.一致性哈希算法:hash模块
  7. 织梦系统的安装与详细信息
  8. 从零学React Native之05混合开发
  9. C#轻量级通通讯组件StriveEngine —— C/S通信开源demo(2) —— 使用二进制协议 (附源码)...
  10. JavaScript 正则表达式
  11. python笔记(一) 数据类型 函数
  12. Linux服务器操作系统查看命令
  13. 【2017.11.16】外包单报价
  14. pytorch系列(八):猫狗大战3-单机多卡无脑训练
  15. python里的jh是啥意思_JH是什么意思啊
  16. 【Spring】1.核心原理解析
  17. Web页面显示随机签名
  18. matlab负无穷大到正无穷大怎么打,matlab中怎么定义n从负无穷到正无?
  19. php实现图片上传和显示,上传和显示图片 - php - 生活点滴
  20. STM32学习--中断

热门文章

  1. 最长公共子序列-动态规划-python
  2. Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'redis.maxIdle' in stri
  3. C++练习11:计算a+aa+aaa+aa…a+…
  4. 区块链学习8:超级账本项目Fabric中的背书、背书节点、背书策略、背书签名
  5. 深度学习笔记(18)- 深度终端之一
  6. linux bash:command,学习使用Linux Command line(Git Bash)
  7. bootstrap学习笔记一: bootstrap初认识,hello bootstrap(下)
  8. arch linux安装ssh,通过ssh远程安装arch linux
  9. GitHub已标星72K阿里内部878页性能优化笔记限时免费
  10. .flo 文件转换为.png 文件 ; matlab 读取 .ppm 和 .flo 文件