使用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 08 Redis
基于Redis位图实现用户签到功能
May 08 Redis
Redis延迟队列和分布式延迟队列的简答实现
May 13 Redis
浅谈Redis主从复制以及主从复制原理
May 29 Redis
Redis可视化客户端小结
Jun 10 Redis
使用redis实现延迟通知功能(Redis过期键通知)
Sep 04 Redis
关于SpringBoot 使用 Redis 分布式锁解决并发问题
Nov 17 Redis
Redis中有序集合的内部实现方式的详细介绍
Mar 16 Redis
在Centos 8.0中安装Redis服务器的教程详解
Mar 21 Redis
Redis安装使用RedisJSON模块的方法
Mar 23 Redis
解决 redis 无法远程连接
May 15 Redis
Redis基本数据类型Set常用操作命令
Jun 01 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&amp;&amp;mysql)五
2006/10/09 PHP
一个简单的php实现的MySQL数据浏览器
2007/03/11 PHP
php自动给网址加上链接的方法
2015/06/02 PHP
解决PHP使用CURL发送GET请求时传递参数的问题
2019/10/11 PHP
JScript 脚本实现文件下载 一般用于下载木马
2009/10/29 Javascript
Ajax 数据请求的简单分析
2011/04/05 Javascript
JS Range HTML文档/文字内容选中、库及应用介绍
2011/05/12 Javascript
13 款最热门的 jQuery 图像 360 度旋转插件推荐
2014/12/09 Javascript
jQuery手机浏览器中拖拽动作的艰难性分析
2015/02/04 Javascript
Jquery实现地铁线路指示灯提示牌效果的方法
2015/03/02 Javascript
使用纯JS代码判断字符串中有多少汉字的实现方法(超简单实用)
2016/11/12 Javascript
深入理解vue.js双向绑定的实现原理
2016/12/05 Javascript
Bootstrap整体框架之CSS12栅格系统
2016/12/15 Javascript
jQuery除指定区域外点击任何地方隐藏DIV功能
2017/11/13 jQuery
js+html5实现手机九宫格密码解锁功能
2018/07/30 Javascript
BootStrap中的模态框(modal,弹出层)功能示例代码
2018/11/02 Javascript
基于vue hash模式微信分享#号的解决
2020/09/07 Javascript
[09:33]2015国际邀请赛第四日TOP10
2015/08/08 DOTA
Python生成器的使用方法和示例代码
2019/03/04 Python
Python的log日志功能及设置方法
2019/07/11 Python
Juicy Couture Beauty官方网站:香水和化妆品
2019/03/12 全球购物
Liu Jo西班牙官网:意大利服装品牌
2019/09/11 全球购物
英国领先的餐饮折扣俱乐部:Gourmet Society
2020/07/26 全球购物
台湾屈臣氏网路商店:Watsons台湾
2020/12/29 全球购物
JSF面试题:Jsf中导航的标签是什么
2013/04/20 面试题
北京银河万佳Java面试题
2012/03/21 面试题
教师党员思想汇报
2014/01/06 职场文书
农村党支部先进事迹
2014/01/14 职场文书
大学生实习证明范文(5篇)
2014/09/18 职场文书
迎新生欢迎词
2015/01/23 职场文书
汉字听写大会观后感
2015/06/12 职场文书
银行资信证明
2015/06/17 职场文书
煤矿隐患排查制度
2015/08/05 职场文书
北京大学中文系教授推荐的10本小说
2019/08/08 职场文书
解决Pytorch中关于model.eval的问题
2021/05/22 Python
使用python求解迷宫问题的三种实现方法
2022/03/17 Python