使用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的几个过期策略
May 27 Redis
浅谈Redis主从复制以及主从复制原理
May 29 Redis
Redis 彻底禁用RDB持久化操作
Jul 09 Redis
Redis如何实现分布式锁
Aug 23 Redis
Springboot/Springcloud项目集成redis进行存取的过程解析
Dec 04 Redis
Redis中有序集合的内部实现方式的详细介绍
Mar 16 Redis
高并发下Redis如何保持数据一致性(避免读后写)
Mar 18 Redis
Redis 哨兵机制及配置实现
Mar 25 Redis
Redis分布式锁的7种实现
Apr 01 Redis
Redis唯一ID生成器的实现
Jul 07 Redis
redis lua限流算法实现示例
Jul 15 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字符编码转换之gb2312转为utf8
2013/10/28 PHP
ajax调用返回php接口返回json数据的方法(必看篇)
2017/05/05 PHP
针对thinkPHP5框架存储过程bug重写的存储过程扩展类完整实例
2018/06/16 PHP
Javascript继承机制的设计思想分享
2011/08/28 Javascript
让人期待的2011年度最佳 jQuery 插件分享
2012/03/16 Javascript
jquery实现动态菜单的实例代码
2013/11/28 Javascript
JS实现鼠标箭头变成一个燃烧烛光效果的方法
2015/02/28 Javascript
基于javascript html5实现多文件上传
2016/03/03 Javascript
无需 Flash 使用 jQuery 复制文字到剪贴板
2016/04/26 Javascript
vue-ajax小封装实例
2017/09/18 Javascript
jQuery时间戳和日期相互转换操作示例
2018/12/07 jQuery
小程序数据通信方法大全(推荐)
2019/04/15 Javascript
js继承的这6种方式!(上)
2019/04/23 Javascript
详解vue 自定义组件使用v-model 及探究其中原理
2019/10/11 Javascript
如何在Vue中使localStorage具有响应式(思想实验)
2020/07/14 Javascript
浅谈vue 组件中的setInterval方法和window的不同
2020/07/30 Javascript
vue实现动态给id赋值,点击事件获取当前点击的元素的id操作
2020/11/09 Javascript
python字典排序实例详解
2015/05/20 Python
python按照多个条件排序的方法
2019/02/08 Python
Python3中编码与解码之Unicode与bytes的讲解
2019/02/28 Python
基于python实现MQTT发布订阅过程原理解析
2020/07/27 Python
汉森批发:Hansen Wholesale
2018/05/24 全球购物
Hoover胡佛官网:美国吸尘器和洗地机品牌
2019/01/09 全球购物
美国在线肉类和海鲜配送:Crowd Cow
2020/10/02 全球购物
党支部书记先进事迹
2014/01/17 职场文书
2014年消防工作实施方案
2014/02/20 职场文书
感恩教育活动总结
2014/05/05 职场文书
新闻工作者先进事迹
2014/05/26 职场文书
群众路线教育实践活动心得体会(四风)
2014/11/03 职场文书
2014年中班下学期工作总结
2014/12/11 职场文书
幼儿园家长反馈意见
2015/06/03 职场文书
子女赡养老人协议书
2016/03/23 职场文书
六年级作文之预言作文
2019/10/25 职场文书
Python基础之数据类型知识汇总
2021/05/18 Python
PHP设计模式(观察者模式)
2021/07/07 PHP
python百行代码实现汉服圈图片爬取
2021/11/23 Python