Redis锁实现防重复提交和并发问题
- @Slf4j
- @Component
- public class RedisLock {
- public static final int LOCK_EXPIRE = 5000;
- @Autowired
- private StringRedisTemplate redisTemplate;
- /**
- * 分布式锁
- *
- * @param key key值
- * @return 是否获取到
- */
- public boolean lock(String key) {
- String lock = key;
- try {
- return (Boolean) redisTemplate.execute((RedisCallback) connection -> {
- long expireAt = System.currentTimeMillis() + LOCK_EXPIRE;
- Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());
- if (acquire) {
- log.info("用户 [{}]加锁成功",key);
- return true;
- } else {
- log.info("用户 [{}]加锁失败",key);
- //判断该key上的值是否过期了
- byte[] value = connection.get(lock.getBytes());
- if (Objects.nonNull(value) && value.length > 0) {
- long expireTime = Long.parseLong(new String(value));
- if (expireTime < System.currentTimeMillis()) {
- // 如果锁已经过期
- byte[] oldValue = connection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE).getBytes());
- // 防止死锁
- return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
- }
- }
- }
- return false;
- });
- } finally {
- RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
- }
- }
- /**
- * 删除锁
- *
- * @param key
- */
- public void delete(String key) {
- try {
- redisTemplate.delete(key);
- } finally {
- RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
- }
- }
- //实现业务
- try{
- // 判断是否获取了锁
- boolean getLock = redisLock(lockKey);
- if(getLock){
- // 此处可以开始写需要实现的代码
- }
- }catch(Exception e){
- e.printStackTrace();
- }finally {
// 判断是否超时了,如果未超时,则释放锁。 超时了,锁有可能被其他线程拿走了,就不做任何操作
redisLock.delete(String.valueOf(caronwerId));
}
}
知识点: redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。
设置成功,返回 1 。 设置失败,返回 0 。
- redis> EXISTS job # job 不存在
- (integer) 0
- redis> SETNX job "programmer" # job 设置成功
- (integer) 1
- redis> SETNX job "code-farmer" # 尝试覆盖 job ,失败
- (integer) 0
- redis> GET job # 没有被覆盖
- "programmer"
- 一.redis命令讲解:
- setnx()命令:
- setnx的含义就是SET if Not Exists,其主要有两个参数 setnx(key, value)。
- 该方法是原子的,如果key不存在,则设置当前key成功,返回1;如果当前key已经存在,则设置当前key失败,返回0。
- get()命令:
- get(key) 获取key的值,如果存在,则返回;如果不存在,则返回nil;
- getset()命令:
- 这个命令主要有两个参数 getset(key, newValue)。该方法是原子的,对key设置newValue这个值,并且返回key原来的旧值。
- 假设key原来是不存在的,那么多次执行这个命令,会出现下边的效果:
- 1. getset(key, “value1”) 返回nil 此时key的值会被设置为value1
- 2. getset(key, “value2”) 返回value1 此时key的值会被设置为value2
- 3. 依次类推!
- 二.具体的使用步骤如下:
- 1. setnx(lockkey, 当前时间+过期超时时间) ,如果返回1,则获取锁成功;如果返回0则没有获取到锁,转向2。
- 2. get(lockkey)获取值oldExpireTime ,并将这个value值与当前的系统时间进行比较,如果小于当前系统时间,则认为这个锁已经超时,可以允许别的请求重新获取,转向3。
- 3. 计算newExpireTime=当前时间+过期超时时间,然后getset(lockkey, newExpireTime) 会返回当前lockkey的值currentExpireTime。
- 4. 判断currentExpireTime与oldExpireTime 是否相等,如果相等,说明当前getset设置成功,获取到了锁。如果不相等,说明这个锁又被别的请求获取走了,那么当前请求可以直接返回失败,或者继续重试。
- 5. 在获取到锁之后,当前线程可以开始自己的业务处理,当处理完毕后,比较自己的处理时间和对于锁设置的超时时间,如果小于锁设置的超时时间,则直接执行delete释放锁;如果大于锁设置的超时时间,则不需要再锁进行处理
Redis锁实现防重复提交和并发问题的更多相关文章
- Java使用Redis实现分布式锁来防止重复提交问题
如何用消息系统避免分布式事务? - 少年阿宾 - BlogJavahttp://www.blogjava.net/stevenjohn/archive/2018/01/04/433004.html [ ...
- 浅谈C#在网络波动时防重复提交
前几天,公司数据库出现了两条相同的数据,而且时间相同(毫秒也相同).排查原因,发现是网络波动造成了重复提交. 由于网络波动而重复提交的例子也比较多: 网络上,防重复提交的方法也很多,使用redis锁, ...
- (亿级流量)分布式防重复提交token设计
大型互联网项目中,很多流量都达到亿级.同一时间很多的人在使用,而每个用户提交表单的时候都可能会出现重复点击的情况,此时如果不做好控制,那么系统将会产生很多的数据重复的问题.怎样去设计一个高可用的防重复 ...
- resubmit 渐进式防重复提交框架简介
resubmit resubmit 是一款为 java 设计的渐进式防止重复提交框架. 推荐阅读: 面试官:你们的项目中是怎么做防止重复提交的? resubmit 渐进式防重复提交框架简介 创作目的 ...
- (九)Struts2 防重复提交
所有的学习我们必须先搭建好Struts2的环境(1.导入对应的jar包,2.web.xml,3.struts.xml) 第一节:重复提交示例演示 struts.xml <?xml version ...
- AJAX防重复提交的办法总结
最近的维护公司的一个代理商平台的时候,客服人员一直反映说的统计信息的时候有重复数据,平台一直都很正常,这个功能是最近新进的一个实习生同事写的功能,然后就排查问题人所在,发现新的这个模块的AJAX提交数 ...
- SpringMVC后台token防重复提交解决方案
本文介绍如何使用token来防止前端重复提交的问题. 目录 1.思路 2.拦截器源码实现 3.注解源码 4.拦截器的配置 5.使用指南 6.结语 思路 1.添加拦截器,拦截需要防重复提交的请求 2.通 ...
- Spring MVC表单防重复提交
利用Spring MVC的过滤器及token传递验证来实现表单防重复提交. 创建注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RU ...
- struts2学习(15)struts2防重复提交
一.重复提交的例子: 模拟一种情况,存在延时啊,系统比较繁忙啊啥的. 模拟延迟5s钟,用户点了一次提交,又点了一次提交,例子中模拟这种情况: 这样会造成重复提交: com.cy.action.St ...
随机推荐
- OKHttp 官方文档【二】
OkHttp 是这几年比较流行的 Http 客户端实现方案,其支持HTTP/2.支持同一Host 连接池复用.支持Http缓存.支持自动重定向 等等,有太多的优点. 一直想找时间了解一下 OkHttp ...
- Win10系统安装MySQL Workbench 8
系统:Window10 专业版 MySQL Workbench 8.0.19 下载地址:https://dev.mysql.com/downloads/workbench/8.0.html 点击Dow ...
- node日志管理 / pm2-logrotate-ext日志管理
本篇文章说的是,如何使用pm2管理node项目的日志输出(切割和备份),文章步骤是基于已经安装了pm2的前提下,没有的,请自行百度. 第一步:需要登录公司服务器,查看一下目前服务器保存的所有日志(用于 ...
- 当asp.net core偶遇docker一(模型验证和Rabbitmq 二)
上一篇我们说到构建了一个Rabbitmq容器 现在我们说说如何在一个悄悄传输消息到队列 我们现在设计一个Rabbitmq发送消息部分的模块 先设计一个远程发送的接口 public interface ...
- python4.2参数传入
#顺序传入参数def show(name,age,sex,hobby):#形参 print("我叫:",name,"年龄:",age,"性别:&quo ...
- MySQL 连接查询汇总
MYSQL-连接查询: # 连接查询:把多张表进行记录的连接(按照某个条件进行数据的拼接) # 分类 1,内链接 2,外连接 # 左外 # 右外 3,自然连接 4,交叉连接 MYSQL-内链接 : # ...
- DRF基础操作流程
Django Rest_Framework 核心思想: 缩减编写api接口的代码 -->DRF Django REST framework是一个建立在Django基础之上的Web 应用开发框架, ...
- Linux根目录下文件夹用途解释
root目录:主要用于存放root用户相关文件的目录: usr目录:安装一个软件时,linux指定的此软件默认安装的目录: home目录:用于存放普通用户的相关文件的目录:例如:我使用adduser ...
- Mybatis 和 Solon 勾搭在一起,也是个漂亮组合
故事相关的源码 https://gitee.com/noear/solon_demo/tree/master/demo08.solon_mybatis 故事开讲 Solon 是Java世界里一个新的极 ...
- Spring Boot 2.x基础教程:使用集中式缓存Redis
之前我们介绍了两种进程内缓存的用法,包括Spring Boot默认使用的ConcurrentMap缓存以及缓存框架EhCache.虽然EhCache已经能够适用很多应用场景,但是由于EhCache是进 ...