【Java】分布式锁Redis和Redisson
- 创业
- 2025-09-08 18:00:02

blog.csdn.net/weixin_44606481/article/details/134373900 .bilibili /video/BV1nW421R7qJ
Redis锁机制一般是由 setnx 命令实现,set if not exists,语法setnx key value,将key设置值为value,如果key不存在会返回1,这种情况下等同 set 命令。 当key存在时,什么也不做会返回0,并且要使用 expire 设置一个锁的过期时间,避免应用程序异常导致锁一直没有释放(或者可以通过 try catch finally来释放锁)。
127.0.0.1:6379> setnx key1 1 (integer) 1 127.0.0.1:6379> setnx key1 1 (integer) 0 127.0.0.1:6379> expire key1 60 (integer) 1 @Override public void testLock() { //从redis里面获取数据 //1 获取当前锁 setnx Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent("lock", "lock"); //2 如果获取到锁,从redis获取数据 数据+1 放回redis里面 if(ifAbsent) { //获取锁成功,执行业务代码 //1.先从redis中通过key num获取值 key提前手动设置 num 初始值:0 String value = redisTemplate.opsForValue().get("num"); //2.如果值为空则非法直接返回即可 if (StringUtils.isBlank(value)) { return; } //3.对num值进行自增加一 int num = Integer.parseInt(value); redisTemplate.opsForValue().set("num", String.valueOf(++num)); //3 释放锁 redisTemplate.delete("lock"); } else { try { Thread.sleep(100); this.testLock(); } catch (InterruptedException e) { e.printStackTrace(); } } } LUA脚本保证原子性不是原子性可能导致的问题:
把index2的分布式锁给删掉了
给分布式锁加上uuid,可以防止删除不是自己的锁,锁误删
//lua脚本保证原子性 @Override public void testLock() { //从redis里面获取数据 String uuid = UUID.randomUUID().toString(); //1 获取当前锁 setnx + 设置过期时间 // Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent("lock", "lock"); Boolean ifAbsent = redisTemplate.opsForValue() .setIfAbsent("lock", uuid,3, TimeUnit.SECONDS); //2 如果获取到锁,从redis获取数据 数据+1 放回redis里面 if(ifAbsent) { //获取锁成功,执行业务代码 //1.先从redis中通过key num获取值 key提前手动设置 num 初始值:0 String value = redisTemplate.opsForValue().get("num"); //2.如果值为空则非法直接返回即可 if (StringUtils.isBlank(value)) { return; } //3.对num值进行自增加一 int num = Integer.parseInt(value); redisTemplate.opsForValue().set("num", String.valueOf(++num)); //出现异常 //3 释放锁 lua脚本实现 DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); //lua脚本 String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" + "then\n" + " return redis.call(\"del\",KEYS[1])\n" + "else\n" + " return 0\n" + "end"; redisScript.setScriptText(script); //设置返回结果 redisScript.setResultType(Long.class); redisTemplate.execute(redisScript, Arrays.asList("lock"),uuid); } else { try { Thread.sleep(100); this.testLock(); } catch (InterruptedException e) { e.printStackTrace(); } } } 锁为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:
第一个:互斥性,在任何时刻,只有一个客户端能持有锁。
第二个:不会发生死锁,即使有一个客户端在获取锁操作时候崩溃了,也能保证其他客户端能获取到锁。
第三个:解铃还须系铃人,解锁加锁必须同一个客户端操作。 (uuid来保证)
第四个:加锁和解锁必须具备原子性
Redisson Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。Redisson的宗旨是:促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。Redisson常见作用
分布式对象:分布式对象简单来说就是存储在Redis中的Java对象。不同的Java应用程序或服务就能够共享和访问这些对象,实现数据共享和数据同步。
分布式集合:简单来说就是将集合放在Redis中,并且可以多个客户端对集合进行操作。Redisson支持常见的分布式数据结构,如List、Set、Map、Queue等,使得多个Java应用程序可以并发地访问和修改这些集合。
分布式锁:通过Redisson,你可以轻松地实现分布式锁,确保在分布式环境中的并发操作的正确性和一致性。
缓存:通过Redisson能够轻松的基于redis实现项目中的缓存
常见算法的分布式实现:Redisson提供了一些常用算法的分布式实现,如分布式信号量、分布式位图、分布式计数器等。
导入依赖
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> </dependency>创建Redisson配置类
@Data @Configuration @ConfigurationProperties(prefix = "spring.data.redis") public class RedissonConfig { private String host; private String password; private String port; private int timeout = 3000; private static String ADDRESS_PREFIX = "redis://"; /** * 自动装配 * */ @Bean RedissonClient redissonSingle() { Config config = new Config(); if(!StringUtils.hasText(host)){ throw new RuntimeException("host is empty"); } SingleServerConfig serverConfig = config.useSingleServer() .setAddress(ADDRESS_PREFIX + this.host + ":" + port) .setTimeout(this.timeout); if(StringUtils.hasText(this.password)) { serverConfig.setPassword(this.password); } return Redisson.create(config); } }在业务方法编写加锁和解锁
@Autowired private RedissonClient redissonClient; //Redisson实现 @Override public void testLock() { //1 通过redisson创建锁对象 RLock lock = redissonClient.getLock("lock1"); //2 尝试获取锁 //(1) 阻塞一直等待直到获取到,获取锁之后,默认过期时间30s lock.lock(); //(2) 获取到锁,锁过期时间10s // lock.lock(10,TimeUnit.SECONDS); //(3) 第一个参数获取锁等待时间 // 第二个参数获取到锁,锁过期时间 // try { // // true // boolean tryLock = lock.tryLock(30, 10, TimeUnit.SECONDS); // } catch (InterruptedException e) { // throw new RuntimeException(e); // } //3 编写业务代码 //1.先从redis中通过key num获取值 key提前手动设置 num 初始值:0 String value = redisTemplate.opsForValue().get("num"); //2.如果值为空则非法直接返回即可 if (StringUtils.isBlank(value)) { return; } //3.对num值进行自增加一 int num = Integer.parseInt(value); redisTemplate.opsForValue().set("num", String.valueOf(++num)); //4 释放锁 lock.unlock(); } Redisson原理1、如果我们指定了锁的超时时间,就发送给Redis执行脚本,进行占锁,默认超时就是我们制定的时间,不会自动续期; 2、如果我们未指定锁的超时时间,就使用 lockWatchdogTimeout = 30 * 1000 【看门狗默认时间】
看门狗的作用:为了保证业务操作 在 有锁的状态下进行。
只要线程一加锁成功,就会启动一个watch dog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程一还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了锁过期释放,业务没执行完问题。
基于Redis的Redisson分布式可重入锁RLock Java对象实现了java.util.concurrent.locks.Lock接口。
如果负责储存这个分布式锁的Redisson节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。
另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。
【Java】分布式锁Redis和Redisson由讯客互联创业栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“【Java】分布式锁Redis和Redisson”
上一篇
数据库基本概念及基本使用
下一篇
003注释