使用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 相关文章推荐
Django使用redis配置缓存的方法
Jun 01 Redis
Windows下redis下载、redis安装及使用教程
Jun 02 Redis
压缩Redis里的字符串大对象操作
Jun 23 Redis
浅谈Redis中的RDB快照
Jun 29 Redis
redis 存储对象的方法对比分析
Aug 02 Redis
关于redisson缓存序列化几枚大坑说明
Aug 04 Redis
Redis的字符串是如何实现的
Oct 24 Redis
Redis Stream类型的使用详解
Nov 11 Redis
使用RedisTemplat实现简单的分布式锁
Nov 20 Redis
Redis监控工具RedisInsight安装与使用
Mar 21 Redis
redis protocol通信协议及使用详解
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
《逃离塔科夫》——“萌新劝退,老手自嗨”的硬核FPS游戏
2020/04/03 其他游戏
生成静态页面的PHP类
2006/07/15 PHP
PHP 高级课程笔记 面向对象
2009/06/21 PHP
PHP常用代码大全(新手入门必备)
2010/06/29 PHP
PHP 面向对象详解
2012/09/13 PHP
PHP用函数嵌入网站访问量计数器
2017/10/27 PHP
使Ext的Template可以解析二层的json数据的方法
2007/12/22 Javascript
js常见表单应用技巧
2008/01/09 Javascript
js刷新框架子页面的七种方法代码
2008/11/20 Javascript
jQuery之end()和pushStack()使用介绍
2012/02/07 Javascript
定时器(setTimeout/setInterval)调用带参函数失效解决方法
2013/03/26 Javascript
详解JavaScript中的blink()方法的使用
2015/06/08 Javascript
JQuery fileupload插件实现文件上传功能
2016/03/18 Javascript
JS实时弹出新消息提示框并有提示音响起的实现代码
2016/04/20 Javascript
javascript cookie基础应用之记录用户名的方法
2016/09/20 Javascript
Js中async/await的执行顺序详解
2017/09/22 Javascript
JavaScript累加、迭代、穷举、递归等常用算法实例小结
2018/05/08 Javascript
JavaScript实现字符串与HTML格式相互转换
2020/03/17 Javascript
用Javascript实现发送短信验证码间隔功能
2021/02/08 Javascript
python利用datetime模块计算时间差
2015/08/04 Python
Python学习笔记之视频人脸检测识别实例教程
2019/03/06 Python
python验证身份证信息实例代码
2019/05/06 Python
TensorBoard 计算图的查看方式
2020/02/15 Python
Python绘图实现台风路径可视化代码实例
2020/10/23 Python
html table呈现个人简历以及单元格宽度失效的问题解决
2021/01/22 HTML / CSS
中国一家专注拼团的社交购物网站:拼多多
2018/06/13 全球购物
JDO的含义
2012/11/17 面试题
护士实习鉴定范文
2013/12/22 职场文书
你懂得怎么写自荐信吗?
2013/12/27 职场文书
毕业自我鉴定怎么写
2014/03/25 职场文书
陈胜吴广起义口号
2014/06/20 职场文书
培根随笔读书笔记
2015/07/01 职场文书
煤矿安全生产工作总结
2015/08/13 职场文书
React Hook用法示例详解(6个常见hook)
2021/04/28 Javascript
深入浅出讲解Java8函数式编程
2022/01/18 Java/Android
springboot实现string转json json里面带数组
2022/06/16 Java/Android