使用RedisTemplat实现简单的分布式锁


Posted in Redis onNovember 20, 2021

不使用redisson框架实现Redis分布式锁 准备工作:

导入依赖

<dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-data-redis</artifactId></dependency>

编写RedisConfig类

@Configurationpublic class RedisConfig {    @Bean    public RedisTemplate<String , Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();        //String类型 key序列器        redisTemplate.setKeySerializer(new StringRedisSerializer());        //String类型 value序列器        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());        //Hash类型 key序列器        redisTemplate.setHashKeySerializer(new StringRedisSerializer());        //Hash类型 value序列器        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());        //将连接工厂注入        redisTemplate.setConnectionFactory(redisConnectionFactory);        return redisTemplate;    } }

1.在SpringBootTest中编写测试模块 1.1:使用占位符加锁:

占位符加锁问题:出现异常时无法释放锁,导致后继进入的线程成为死锁

@SpringBootTestclass ApplicationTests {    @Autowired    private RedisTemplate redisTemplate;@Testpublic void lodsTest01(){ValueOperations valueOperations = redisTemplate.opsForValue();        //创建一个占位符,如果key不存在才可以设置成功        Boolean isLock = valueOperations.setIfAbsent("k1", "v1");        //如果占位成功,进行正常操作        if (isLock){        //设置一个name存到redis            valueOperations.set("name","xxxx");            //从redis取出name            String name = (String) valueOperations.get("name");            System.out.println("name = " + name);            //手动制造异常            Integer.parseInt("xxxx");            //操作结束删除锁            redisTemplate.delete("k1");        }else{            System.out.println("有线程在用,请稍后在试");        }}}

测试
第一个线程出现异常无法释放锁:
使用RedisTemplat实现简单的分布式锁
之后所有线程都无法访问:
使用RedisTemplat实现简单的分布式锁

解决方案为锁加一个有效时间。

1.2:使用占位符设置有效时间解决死锁问题:

占位符设置有效时间问题即使某线程出现异常,但占位符过了有效时间,锁就会释放。但是在大量线程同时访问时,如果线程1被外界因素影响(网络波动,服务器出问题等等),线程1的业务还没完成,但锁的有效时间到了的话,下一个线程就会进来,就会出现线程不安全的情况,出现线程互相删锁的情况。

@Test    public void testLock02()  {        ValueOperations valueOperations = redisTemplate.opsForValue();        //如果key不存在才可以设置成功,设置一个有效时间防止线程异常出现死锁        Boolean isLock = valueOperations.setIfAbsent("k1", "v1",5, TimeUnit.SECONDS);        //如果占位成功,进行正常操作        if (isLock){        //设置一个name存到redis            valueOperations.set("name","xxxx");            //从redis取出name            String str = (String) valueOperations.get("name");            System.out.println("name = " + str);            //制造异常            Integer.parseInt("xxxx");            //操作结束删除锁            redisTemplate.delete("k1");        }else{            System.out.println("有线程在用,请稍后在试");        }    }

解决方案: 使用lua脚本,给每个锁的key对应的value设置一个随机数

1.3:使用lua脚本解决线程不安全问题:

lua脚本可以写在Redis服务器上:
优点: 在服务器上运行速度快

缺点: 修改代码时比较麻烦

lua脚本可以通过java发送
优点: 修改代码方便

缺点: 每次发送请求时都需要占用网络资源

1.3.1:编写lua脚本 使用RedisTemplat实现简单的分布式锁

if redis.call("get",KEYS[1])==ARGV[1] then    return redis.call("del",KEYS[1])else    return 0end

1.3.2:修改ReidsConfig类

@Bean    public DefaultRedisScript<Boolean> defaultRedisScript(){        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();        //lock.lua脚本位置和application.yml同级目录        redisScript.setLocation(new ClassPathResource("lock.lua"));        //设置类型为boolean        redisScript.setResultType(Boolean.class);        return redisScript;    }

1.3.3:编写测试模块

@Test    public void testLock03(){        ValueOperations valueOperations = redisTemplate.opsForValue();        String value = UUID.randomUUID().toString();        //如果key不存在才可以设置成功,设置一个value为随机数的值,防止出现线程太多 导致线程不安全        Boolean isLock = valueOperations.setIfAbsent("k1", value, 5, TimeUnit.SECONDS);        //如果占位成功,进行正常操作         if (isLock){        //设置一个name存到redis            valueOperations.set("name","xxxx");            //从redis取出name            String name = (String) valueOperations.get("name");            System.out.println("name = " + name);            //为redis发送lua脚本删除锁对应的value            Boolean aBoolean = (Boolean) redisTemplate.execute(redisScript, Collections.singletonList("k1"), value);            System.out.println(aBoolean);        }else{            System.out.println("有线程在用,请稍后在试");        }    }

测试结果:
顺利把name值存到redis中并把锁删除并返回true
使用RedisTemplat实现简单的分布式锁
锁会被正常删除只留下name:
使用RedisTemplat实现简单的分布式锁

Redis 相关文章推荐
Redis持久化与主从复制的实践
Apr 27 Redis
浅谈Redis的几个过期策略
May 27 Redis
redis使用不当导致应用卡死bug的过程解析
Jul 01 Redis
Redis源码阅读:Redis字符串SDS详解
Jul 15 Redis
Redis 常见使用场景
Aug 30 Redis
详解Redis在SpringBoot工程中的综合应用
Oct 16 Redis
Redis的字符串是如何实现的
Oct 24 Redis
sentinel支持的redis高可用集群配置详解
Apr 01 Redis
Redis高并发缓存架构性能优化
May 15 Redis
浅谈Redis缓冲区机制
Jun 05 Redis
关于Redis的主从复制及哨兵问题
Jun 16 Redis
Redis+AOP+自定义注解实现限流
Jun 28 Redis
redis缓存存储Session原理机制
CentOS8.4安装Redis6.2.6的详细过程
SpringBoot整合Redis入门之缓存数据的方法
Nov 17 #Redis
Window server中安装Redis的超详细教程
关于SpringBoot 使用 Redis 分布式锁解决并发问题
Redis Stream类型的使用详解
Redis 持久化 RDB 与 AOF的执行过程
You might like
BBS(php &amp; mysql)完整版(二)
2006/10/09 PHP
用PHP控制用户的浏览器--ob*函数的使用说明
2007/03/16 PHP
destoon供应信息title调用出公司名称的方法
2014/08/22 PHP
PHP微信开发之模板消息回复
2016/06/24 PHP
PHP面向对象程序设计高级特性详解(接口,继承,抽象类,析构,克隆等)
2016/12/02 PHP
PHP7基于curl实现的上传图片功能
2018/05/11 PHP
JavaScript进阶教程(第四课第一部分)
2007/04/05 Javascript
XML+XSL 与 HTML 两种方案的结合
2007/04/22 Javascript
JQuery中serialize()用法实例分析
2015/02/06 Javascript
jQuery绑定自定义事件的魔法升级版
2016/06/30 Javascript
javascript比较语义化版本号的实现代码
2016/09/09 Javascript
jquery自定义插件结合baiduTemplate.js实现异步刷新(附源码)
2016/12/22 Javascript
jQuery解析返回的xml和json方法详解
2017/01/05 Javascript
jQuery DOM节点的遍历方法小结
2017/08/15 jQuery
jQuery选择器之子元素过滤选择器
2017/09/28 jQuery
基于jquery实现五星好评
2017/11/18 jQuery
JavaScript实现的贝塞尔曲线算法简单示例
2018/01/30 Javascript
vue.js学习笔记之v-bind和v-on解析
2018/05/03 Javascript
详解Python中的strftime()方法的使用
2015/05/22 Python
利用Python暴力破解zip文件口令的方法详解
2017/12/21 Python
python实现顺序表的简单代码
2018/09/28 Python
python自动发送测试报告邮件功能的实现
2019/01/22 Python
OpenCV+Python识别车牌和字符分割的实现
2019/01/31 Python
python 如何读、写、解析CSV文件
2021/03/03 Python
在HTML5 Canvas中放入图片和保存为图片的方法
2014/05/03 HTML / CSS
阿姆斯特丹城市卡:Amsterdam Pass
2019/12/01 全球购物
某/etc/fstab文件中的某行如下: /dev/had5 /mnt/dosdata msdos defaults,usrquota 1 2 请解释其含义
2013/09/18 面试题
铁路工务反思材料
2014/02/07 职场文书
应聘文员自荐信范文
2014/03/11 职场文书
求职信结尾怎么写
2014/05/26 职场文书
单位消防安全责任书
2014/07/23 职场文书
2014小学一年级班主任工作总结
2014/12/05 职场文书
2015年会计年终工作总结
2015/05/26 职场文书
大学生入党自我鉴定范文
2019/06/21 职场文书
python blinker 信号库
2022/05/04 Python
redis lua限流算法实现示例
2022/07/15 Redis