1. @Slf4j
  2. @Component
  3. public class RedisLock {
  4.  
  5. public static final int LOCK_EXPIRE = 5000;
  6.  
  7. @Autowired
  8. private StringRedisTemplate redisTemplate;
  9.  
  10. /**
  11. * 分布式锁
  12. *
  13. * @param key key值
  14. * @return 是否获取到
  15. */
  16. public boolean lock(String key) {
  17.  
  18. String lock = key;
  19. try {
  20. return (Boolean) redisTemplate.execute((RedisCallback) connection -> {
  21. long expireAt = System.currentTimeMillis() + LOCK_EXPIRE;
  22. Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());
  23. if (acquire) {
  24. log.info("用户 [{}]加锁成功",key);
  25.  
  26. return true;
  27. } else {
  28. log.info("用户 [{}]加锁失败",key);
  29.  
  30. //判断该key上的值是否过期了
  31. byte[] value = connection.get(lock.getBytes());
  32. if (Objects.nonNull(value) && value.length > 0) {
  33. long expireTime = Long.parseLong(new String(value));
  34. if (expireTime < System.currentTimeMillis()) {
  35. // 如果锁已经过期
  36. byte[] oldValue = connection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE).getBytes());
  37. // 防止死锁
  38. return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
  39. }
  40. }
  41. }
  42. return false;
  43. });
  44. } finally {
  45. RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
  46. }
  47. }
  48.  
  49. /**
  50. * 删除锁
  51. *
  52. * @param key
  53. */
  54. public void delete(String key) {
  55. try {
  56. redisTemplate.delete(key);
  57. } finally {
  58. RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
  59. }
  60. }
  1. //实现业务
  2.  
  3. try{
  4. // 判断是否获取了锁
  5. boolean getLock = redisLock(lockKey);
  6. if(getLock){
  7. // 此处可以开始写需要实现的代码
  8.  
  9. }
  10. }catch(Exception e){
  1.       e.printStackTrace();
  1. }finally {
    // 判断是否超时了,如果未超时,则释放锁。 超时了,锁有可能被其他线程拿走了,就不做任何操作
    redisLock.delete(String.valueOf(caronwerId));
    }
    }

知识点: redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。

设置成功,返回 1 。 设置失败,返回 0 。

  1. redis> EXISTS job # job 不存在
  2. (integer) 0
  3.  
  4. redis> SETNX job "programmer" # job 设置成功
  5. (integer) 1
  6.  
  7. redis> SETNX job "code-farmer" # 尝试覆盖 job ,失败
  8. (integer) 0
  9.  
  10. redis> GET job # 没有被覆盖
  11. "programmer"
  1. 一.redis命令讲解:
  2. setnx()命令:
  3. setnx的含义就是SET if Not Exists,其主要有两个参数 setnx(key, value)。
  4.  
  5. 该方法是原子的,如果key不存在,则设置当前key成功,返回1;如果当前key已经存在,则设置当前key失败,返回0
  6.  
  7. get()命令:
  8. get(key) 获取key的值,如果存在,则返回;如果不存在,则返回nil
  9. getset()命令:
  10. 这个命令主要有两个参数 getset(key, newValue)。该方法是原子的,对key设置newValue这个值,并且返回key原来的旧值。
  11. 假设key原来是不存在的,那么多次执行这个命令,会出现下边的效果:
  12. 1. getset(key, value1”) 返回nil 此时key的值会被设置为value1
  13. 2. getset(key, value2”) 返回value1 此时key的值会被设置为value2
  14. 3. 依次类推!
  15. 二.具体的使用步骤如下:
  16. 1. setnx(lockkey, 当前时间+过期超时时间) ,如果返回1,则获取锁成功;如果返回0则没有获取到锁,转向2
  17. 2. get(lockkey)获取值oldExpireTime ,并将这个value值与当前的系统时间进行比较,如果小于当前系统时间,则认为这个锁已经超时,可以允许别的请求重新获取,转向3
  18. 3. 计算newExpireTime=当前时间+过期超时时间,然后getset(lockkey, newExpireTime) 会返回当前lockkey的值currentExpireTime
  19. 4. 判断currentExpireTimeoldExpireTime 是否相等,如果相等,说明当前getset设置成功,获取到了锁。如果不相等,说明这个锁又被别的请求获取走了,那么当前请求可以直接返回失败,或者继续重试。
  20. 5. 在获取到锁之后,当前线程可以开始自己的业务处理,当处理完毕后,比较自己的处理时间和对于锁设置的超时时间,如果小于锁设置的超时时间,则直接执行delete释放锁;如果大于锁设置的超时时间,则不需要再锁进行处理

Redis锁实现防重复提交和并发问题的更多相关文章

  1. Java使用Redis实现分布式锁来防止重复提交问题

    如何用消息系统避免分布式事务? - 少年阿宾 - BlogJavahttp://www.blogjava.net/stevenjohn/archive/2018/01/04/433004.html [ ...

  2. 浅谈C#在网络波动时防重复提交

    前几天,公司数据库出现了两条相同的数据,而且时间相同(毫秒也相同).排查原因,发现是网络波动造成了重复提交. 由于网络波动而重复提交的例子也比较多: 网络上,防重复提交的方法也很多,使用redis锁, ...

  3. (亿级流量)分布式防重复提交token设计

    大型互联网项目中,很多流量都达到亿级.同一时间很多的人在使用,而每个用户提交表单的时候都可能会出现重复点击的情况,此时如果不做好控制,那么系统将会产生很多的数据重复的问题.怎样去设计一个高可用的防重复 ...

  4. resubmit 渐进式防重复提交框架简介

    resubmit resubmit 是一款为 java 设计的渐进式防止重复提交框架. 推荐阅读: 面试官:你们的项目中是怎么做防止重复提交的? resubmit 渐进式防重复提交框架简介 创作目的 ...

  5. (九)Struts2 防重复提交

    所有的学习我们必须先搭建好Struts2的环境(1.导入对应的jar包,2.web.xml,3.struts.xml) 第一节:重复提交示例演示 struts.xml <?xml version ...

  6. AJAX防重复提交的办法总结

    最近的维护公司的一个代理商平台的时候,客服人员一直反映说的统计信息的时候有重复数据,平台一直都很正常,这个功能是最近新进的一个实习生同事写的功能,然后就排查问题人所在,发现新的这个模块的AJAX提交数 ...

  7. SpringMVC后台token防重复提交解决方案

    本文介绍如何使用token来防止前端重复提交的问题. 目录 1.思路 2.拦截器源码实现 3.注解源码 4.拦截器的配置 5.使用指南 6.结语 思路 1.添加拦截器,拦截需要防重复提交的请求 2.通 ...

  8. Spring MVC表单防重复提交

    利用Spring MVC的过滤器及token传递验证来实现表单防重复提交. 创建注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RU ...

  9. struts2学习(15)struts2防重复提交

    一.重复提交的例子: 模拟一种情况,存在延时啊,系统比较繁忙啊啥的. 模拟延迟5s钟,用户点了一次提交,又点了一次提交,例子中模拟这种情况: 这样会造成重复提交:   com.cy.action.St ...

随机推荐

  1. OKHttp 官方文档【二】

    OkHttp 是这几年比较流行的 Http 客户端实现方案,其支持HTTP/2.支持同一Host 连接池复用.支持Http缓存.支持自动重定向 等等,有太多的优点. 一直想找时间了解一下 OkHttp ...

  2. Win10系统安装MySQL Workbench 8

    系统:Window10 专业版 MySQL Workbench 8.0.19 下载地址:https://dev.mysql.com/downloads/workbench/8.0.html 点击Dow ...

  3. node日志管理 / pm2-logrotate-ext日志管理

    本篇文章说的是,如何使用pm2管理node项目的日志输出(切割和备份),文章步骤是基于已经安装了pm2的前提下,没有的,请自行百度. 第一步:需要登录公司服务器,查看一下目前服务器保存的所有日志(用于 ...

  4. 当asp.net core偶遇docker一(模型验证和Rabbitmq 二)

    上一篇我们说到构建了一个Rabbitmq容器 现在我们说说如何在一个悄悄传输消息到队列 我们现在设计一个Rabbitmq发送消息部分的模块 先设计一个远程发送的接口 public interface ...

  5. python4.2参数传入

    #顺序传入参数def show(name,age,sex,hobby):#形参 print("我叫:",name,"年龄:",age,"性别:&quo ...

  6. MySQL 连接查询汇总

    MYSQL-连接查询: # 连接查询:把多张表进行记录的连接(按照某个条件进行数据的拼接) # 分类 1,内链接 2,外连接 # 左外 # 右外 3,自然连接 4,交叉连接 MYSQL-内链接 : # ...

  7. DRF基础操作流程

    Django Rest_Framework 核心思想: 缩减编写api接口的代码 -->DRF Django REST framework是一个建立在Django基础之上的Web 应用开发框架, ...

  8. Linux根目录下文件夹用途解释

    root目录:主要用于存放root用户相关文件的目录: usr目录:安装一个软件时,linux指定的此软件默认安装的目录: home目录:用于存放普通用户的相关文件的目录:例如:我使用adduser ...

  9. Mybatis 和 Solon 勾搭在一起,也是个漂亮组合

    故事相关的源码 https://gitee.com/noear/solon_demo/tree/master/demo08.solon_mybatis 故事开讲 Solon 是Java世界里一个新的极 ...

  10. Spring Boot 2.x基础教程:使用集中式缓存Redis

    之前我们介绍了两种进程内缓存的用法,包括Spring Boot默认使用的ConcurrentMap缓存以及缓存框架EhCache.虽然EhCache已经能够适用很多应用场景,但是由于EhCache是进 ...