使用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中一个String类型引发的惨案
Jul 25 Redis
Redis读写分离搭建的完整步骤
Sep 14 Redis
使用RedisTemplat实现简单的分布式锁
Nov 20 Redis
Redis之RedisTemplate配置方式(序列和反序列化)
Mar 13 Redis
高并发下Redis如何保持数据一致性(避免读后写)
Mar 18 Redis
Redis监控工具RedisInsight安装与使用
Mar 21 Redis
redis调用二维码时的不断刷新排查分析
Apr 01 Redis
sentinel支持的redis高可用集群配置详解
Apr 01 Redis
详解Redis的三种常用的缓存读写策略步骤
May 06 Redis
Redis特殊数据类型bitmap位图
Jun 01 Redis
基于redis+lua进行限流的方法
Jul 23 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
桌面中心(四)数据显示
2006/10/09 PHP
php处理文件的小例子(解压缩,删除目录)
2013/02/03 PHP
PHP 输出URL的快捷方式示例代码
2013/09/22 PHP
php生成高清缩略图实例详解
2015/12/07 PHP
WordPress开发中短代码的实现及相关函数使用技巧
2016/01/05 PHP
Laravel中基于Artisan View扩展包创建及删除应用视图文件的方法
2016/10/08 PHP
PHP常用日期加减计算方法实例小结
2018/07/31 PHP
phpStorm+XDebug+chrome 配置详解
2019/04/01 PHP
js判断运行jsp页面的浏览器类型以及版本示例
2013/10/30 Javascript
javascript四舍五入函数代码分享(保留后几位)
2013/12/10 Javascript
js中replace的用法总结
2013/12/27 Javascript
Javascript 按位与运算符 (&amp;)使用介绍
2014/02/04 Javascript
浅析Node.js:DNS模块的使用
2016/11/23 Javascript
原生js中ajax访问的实例详解
2017/09/19 Javascript
vue使用axios跨域请求数据问题详解
2017/10/18 Javascript
浅谈JsonObject中的key-value数据解析排序问题
2017/12/06 Javascript
javascript将非数值转换为数值
2018/09/13 Javascript
vue-cli 脚手架基于Nightwatch的端到端测试环境的过程
2018/09/30 Javascript
layui默认选中table的CheckBox复选框方法
2019/09/19 Javascript
[17:36]VG战队纪录片
2014/08/21 DOTA
[01:12:53]完美世界DOTA2联赛PWL S2 Forest vs SZ 第一场 11.25
2020/11/26 DOTA
Python找出文件中使用率最高的汉字实例详解
2015/06/03 Python
python画一个玫瑰和一个爱心
2020/08/18 Python
微信小程序python用户认证的实现
2019/07/29 Python
详解Python用三种方式统计词频的方法
2019/07/29 Python
Python适配器模式代码实现解析
2019/08/02 Python
Transpose 数组行列转置的限制方式
2020/02/11 Python
python numpy矩阵信息说明,shape,size,dtype
2020/05/22 Python
python json.dumps() json.dump()的区别详解
2020/07/14 Python
如何利用python进行时间序列分析
2020/08/04 Python
css3实现信纸/同学录效果的示例代码
2018/12/11 HTML / CSS
西班牙自行车和跑步商店:Alltricks
2018/07/07 全球购物
Nordgreen手表德国官方网站:丹麦极简主义手表
2019/10/31 全球购物
世界上最大的艺术社区:SAA
2020/12/30 全球购物
预备党员党课思想汇报
2014/01/13 职场文书
昆虫记读书笔记
2015/06/26 职场文书