使用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 相关文章推荐
Redis5之后版本的高可用集群搭建的实现
Apr 27 Redis
Redis IP地址的绑定的实现
May 08 Redis
redis 限制内存使用大小的实现
May 08 Redis
redis 存储对象的方法对比分析
Aug 02 Redis
Redis Stream类型的使用详解
Nov 11 Redis
基于Redis zSet实现滑动窗口对短信进行防刷限流的问题
Feb 12 Redis
解决linux下redis数据库overcommit_memory问题
Feb 24 Redis
Redis监控工具RedisInsight安装与使用
Mar 21 Redis
解决redis批量删除key值的问题
Mar 23 Redis
Redis基本数据类型Set常用操作命令
Jun 01 Redis
Redis全局ID生成器的实现
Jun 05 Redis
python中使用redis用法详解
Dec 24 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 无限极分类
2008/03/27 PHP
解析wamp5下虚拟机配置文档
2013/06/27 PHP
PHP中使用GD库创建圆形饼图的例子
2014/11/19 PHP
PHP扩展mcrypt实现的AES加密功能示例
2019/01/29 PHP
php下的原生ajax请求用法实例分析
2020/02/28 PHP
EXTJS内使用ACTIVEX控件引起崩溃问题的解决方法
2010/03/31 Javascript
js中document.write使用过程中的一点疑问解答
2014/03/20 Javascript
Jquery图片延迟加载插件jquery.lazyload.js的使用方法
2014/05/21 Javascript
基于jQuery实现顶部导航栏功能
2016/12/27 Javascript
jQuery+Cookie实现切换皮肤功能【附源码下载】
2018/03/25 jQuery
基于vue.js实现分页查询功能
2018/12/29 Javascript
微信小程序scroll-view锚点链接滚动跳转功能
2019/12/12 Javascript
vue实现广告栏上下滚动效果
2020/11/26 Vue.js
[01:03:27]NAVI vs EG 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
利用Python为iOS10生成图标和截屏
2016/09/24 Python
Python cookbook(数据结构与算法)将多个映射合并为单个映射的方法
2018/04/19 Python
Python3实现的爬虫爬取数据并存入mysql数据库操作示例
2018/06/06 Python
树莓派动作捕捉抓拍存储图像脚本
2019/06/22 Python
使用Python画股票的K线图的方法步骤
2019/06/28 Python
anaconda如何查看并管理python环境
2019/07/05 Python
Django 迁移、操作数据库的方法
2019/08/02 Python
实例教程 利用html5和css3打造一款创意404页面
2014/10/20 HTML / CSS
全球知名的珠宝首饰品牌:Kay Jewelers
2018/02/11 全球购物
销售主管岗位职责
2014/02/08 职场文书
网络管理员岗位职责
2014/03/17 职场文书
乔丹名人堂演讲稿
2014/05/24 职场文书
六一儿童节活动总结
2014/08/27 职场文书
纪念九一八事变演讲稿:牢记九一八,屈辱怎能忘
2014/09/14 职场文书
教师业务学习材料
2014/12/16 职场文书
先进工作者事迹材料
2014/12/23 职场文书
社区文明倡议书
2015/04/28 职场文书
教师师德工作总结2015
2015/07/22 职场文书
2016党员发展对象培训心得体会
2016/01/08 职场文书
导游词之台湾阿里山
2019/10/23 职场文书
Mysql数据库值的添加、修改、删除及清空操作实例
2021/06/20 MySQL
Python 文字识别
2022/05/11 Python