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 ...
随机推荐
- __STL_VOLATILE
_Obj* __STL_VOLATILE* __my_free_list = _S_free_list + _S_freelist_index(__n); _Obj* * __my_free_list ...
- Java日志框架(二)
最流行的日志框架解决方案 按笔者理解,现在最流的日志框架解决方案莫过于SLF4J + LogBack.其有以下几个优点: LogBack 自身实现了 SLF4J 的日志接口,不需要 SLF4J 去做进 ...
- 20、Java 泛型
1.晓之以理,动之以码 学Java就是很上头哦,一来直接三连问!!! 什么是泛型?为什么要用泛型?泛型怎么用? 当然泛型在Java中有很重要的一个地位,在面向对象编程以及在各种设计模式中有非常广泛的应 ...
- Docker 启动 Nginx
Docker 启动 Nginx 拉取镜像 docker pull nginx:1.17.9 启动步骤 # 创建 nginx 目录 mkdir -p /usr/local/nginx && ...
- 聊聊MySQL主从复制的几种复制方式
目录 异步复制 多线程复制 增强半同步复制 异步复制 MySQL的复制默认是异步的,主从复制至少需要两个MYSQL服务,这些MySQL服务可以分布在不同的服务器上,也可以在同一台服务器上. MySQL ...
- kafka-clients 1.0 内部请求接口文档
AddOffsetsToTxnRequest version:0 name type defaultValue docString transactional_id STRING null The t ...
- 【原创】Linux虚拟化KVM-Qemu分析(一)
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: KVM版本:5.9 ...
- python接口自动化 - 断言(上)
我们在做接口自动化的时候会用当unittest框架,这个框架中是有assert方法 当我们写好我们的case后 总要有个验证是否正确的东西,assert就给我们提供了非常强大的结果验证 序号 断言方法 ...
- RPC 框架 Dubbo 从理解到使用(一)
技术架构演变 单一应用架构 通俗地讲,"单体应用(monolith application)"就是将应用程序的所有功能都打包成一个独立的单元.当网站流量很小时,只需一个应用,将所有 ...
- Vue3.0数据响应式原理
在vue2版本中响应式使用的是ES5对象的操作,通过遍历对象Object.defineProperty属性值的变化,实现监听数据 在3.0中使用的ES6版本的Proxy代理对象方式来实现数据的监听,省 ...