使用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
Django使用redis配置缓存的方法
Jun 01 Redis
redis cluster支持pipeline的实现思路
Jun 23 Redis
Redisson实现Redis分布式锁的几种方式
Aug 07 Redis
基于Redis结合SpringBoot的秒杀案例详解
Oct 05 Redis
linux下安装redis图文详细步骤
Dec 04 Redis
Redis如何实现验证码发送 以及限制每日发送次数
Apr 18 Redis
Redis数据同步之redis shake的实现方法
Apr 21 Redis
Redis基本数据类型List常用操作命令
Jun 01 Redis
Redis基本数据类型String常用操作命令
Jun 01 Redis
利用Redis实现点赞功能的示例代码
Jun 28 Redis
redis lua限流算法实现示例
Jul 15 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
php中关于普通表单多文件上传的处理方法
2011/03/25 PHP
PHP判断远程url是否有效的几种方法小结
2011/10/08 PHP
PHP 使用header函数设置HTTP头的示例解析 表头
2013/06/17 PHP
ThinkPHP实现事务回滚示例代码
2014/06/23 PHP
php+mysqli使用面向对象方式更新数据库实例
2015/01/29 PHP
微信支付的开发流程详解
2016/09/13 PHP
Web版彷 Visual Studio 2003 颜色选择器
2007/01/09 Javascript
javascript 遍历验证所有文本框的值
2009/08/27 Javascript
也说JavaScript中String类的replace函数
2011/09/22 Javascript
node.js中Socket.IO的进阶使用技巧
2014/11/04 Javascript
纯JS单页面赛车游戏制作代码分享
2017/03/03 Javascript
解决JSON.stringify()自动将中文转译成unicode的问题
2018/01/05 Javascript
vue 2.1.3 实时显示当前时间,每秒更新的方法
2018/09/16 Javascript
js中null与空字符串&quot;&quot;的区别讲解
2019/01/17 Javascript
vue下载excel的实现代码后台用post方法
2019/05/10 Javascript
基于vue实现圆形菜单栏组件
2019/07/05 Javascript
JavaScript实现4位随机验证码的生成
2021/01/28 Javascript
python轻松查到删除自己的微信好友
2016/01/10 Python
python利用有道翻译实现&quot;语言翻译器&quot;的功能实例
2017/11/14 Python
Python聊天室程序(基础版)
2018/04/01 Python
Python3 中把txt数据文件读入到矩阵中的方法
2018/04/27 Python
flask框架实现连接sqlite3数据库的方法分析
2018/07/16 Python
django实现类似触发器的功能
2019/11/15 Python
python打印文件的前几行或最后几行教程
2020/02/13 Python
树莓派4B安装Tensorflow的方法步骤
2020/07/16 Python
python 实现性别识别
2020/11/21 Python
关于css兼容性问题及一些常见问题汇总
2016/05/03 HTML / CSS
HTML5本地存储和本地数据库实例详解
2017/09/05 HTML / CSS
如何执行一个shell程序
2012/11/23 面试题
高中毕业自我鉴定
2013/12/13 职场文书
《蚂蚁和蝈蝈》教学反思
2014/02/24 职场文书
2014年幼儿园重阳节活动方案
2014/09/16 职场文书
个人汇报材料范文
2014/12/30 职场文书
责任书格式
2019/04/18 职场文书
2019年健身俱乐部的创业计划书
2019/08/26 职场文书
聊聊Python中关于a=[[]]*3的反思
2021/06/02 Python