参考Redisson-分布式锁
问题分析
在集群模式下或者分布式的系统下,存在多个JVM,不同JVM之间用的是不同的锁监视器,也就意味着可能产生并行而锁不生效的问题,也就导致了线程并发安全问题,为此就需要一种锁监视器,可以让不同的JVM之间使用,也就需要分布式锁
基本原理
- 什么是分布式锁:满足分布式系统或者集群模式下多进程可见并互斥的锁
- 特点:
- 多进程可见
- 互斥
- 高可用
- 高性能(高并发)
- 安全性
分布式锁的实现(常见)

redis常见的实现之一就是通过Redisson
Redisson快速开始
- 引入依赖:
1 2 3 4 5
| <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.16.2</version> </dependency>
|
- 配置Redission客户端:
1 2 3 4 5 6 7 8 9 10 11 12
| @Configuration public class RedissonConfig{ @Bean public RedissonClient redissonClient(){ Config config = new Config(); config.useSingleServer().setAddress("redis://192.168.150.101:6379").setPassword("123321"); return Redisson.create(config); } }
|
- 使用Redisson的分布式锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Resouuce private RedissonClient redissonClient
@Test void testRedisson() throws InterruptedException{ RLock lock = redissonClient.getLock("anyLock"); boolean isLock = lock.tryLock(1, 10, TimeUnit.SECONDS); if(isLock){ try{ System.out.println("执行业务"); }finally{ lock.unlock(); } } }
|
Redisson可重入锁原理
- 可重入就是利用hash结构记录线程id和重入次数
- 流程图如下:

获取锁的Lua脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| local key = KEYS[1]; local threadId = ARGV[1]; local releaseTime = ARGV[2];
if (redis.call('exists', key) == 0) then redis.call('hset', key, threadId, '1'); redis.call('expire', key, releaseTime); return 1; end;
if (redis.call('hexists', key, threadId) == 1) then redis.call('hincrby', key, thread, 1); redis.call('expire', key, releaseTime); return 1; end; return 0;
|
释放锁的Lua脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| local key = KEYS[1]; local threadId = ARGV[1]; local releaseTime = ARGV[2];
if (redis.call('HEXISTS', key, threadId) == 0) then return nil; end;
local count = redis.call('hincrby', key, threadId, -1);
if (count > 0) then redis.call('expire', key, releaseTime); return nil; else redis.call('del', key); return nil; end;
|
Redisson的锁重试和WatchDog机制
流程图:

重试机制
- 默认的锁超时时间是三十秒(WathchdogTimeout)
- 利用消息订阅(PubSub)和信号量的机制,实现等待、唤醒、获取锁失败的重试机制,每次都等待释放后再尝试,减少了cpu的无用消耗
WatchDog机制
问题产生:如果锁超时释放了,如何防止另一个在重试锁而不会误拿到锁
在获取锁成功后,调用了scheduleExpirationRenewal这个方法,当新的线程来的时候,会执行一个续约方法,其中有一个超时任务(Timeout task),就会在**十秒(releaseTime/3)**后执行,更新有效期,然后又递归,保证锁永不过期,一直到锁释放(unlock)的时候,就会去取消这个超时任务,删除干净
注意:如果自己设置了leaseTime则就不会开启看门狗
mutiLock原理-主从一致性
产生原因-原理
主从集群时,主节点宕机产生的锁不同步问题,就需要多个节点集群(可附带主从),这时候必须所有主节点都有锁才会返回
创建mutiLock
- 首页在redis中部署多个实例
- 在java中配置好后,注入
redissonClient
- 使用
lock = redissonClient.getMultilock(lock1,lock2,lock3);连锁
lock1,lock2,lock3是注入实例的名称
缺点