今天做任务用到了redis的分布式锁,分享一下,不足之处还请大佬指正。
使用redis,我们想要自己实现分布式锁,首先要防止死锁、占用时间过长等问题,以下是分布式锁的流程,分为两个线程,左半边为主线程,右半边为守护线程。
由于我并没有用这种方法做,只是设计了一下,就不战列具体代码,只说思路。
首先我们要封装三个方法,降低耦合性,设置参数的时候要考虑灵活性,这样其他的代码也可以使用。
这里的添加锁和释放锁时的操作代码尽量使用lua脚本语言编写,来保证操作的原子性,防止执行过程中服务器宕机。
第一个方法tryGetDistributedLock(),尝试获取分布式锁。参数已经列出来了,key值就是你要锁的对象的key值。request建议使用uuid生成,存活时间就是到了多久之后,不管你的程序有没有执行结束,我都要把这个锁释放。返回boolean值,添加成功返回true,失败返回false,很多个线程同时来操作这个方法,但是只有第一个线程能成功,添加锁得到true,其余的线程都会得到false。
/**
* 尝试获取分布式锁
*
* @param jedis Redis客户端
* @param lockKey key值
* @param requestId 请求标识
* @param expireTime 存活时间
* @return 是否获取成功
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime){}
第二个方法releaseDistributedLock(),释放分布式锁。key值就是你要锁的对象得key值。request使用添加锁时候生成的uuid,用来防止其他用户来释放你的锁,释放时,必须lockKey和requestId都和添加锁时一致才可以释放。
/**
* 释放分布式锁
*
* @param jedis Redis客户端
* @param lockKey key值
* @param requestId 请求标识
* @return 是否释放成功
*/
public static boolean releaseDistributedLock(Jedis jedis,String lockKey,String requestId){}
第三个方法启动守护线程。启动另一个线程,这里关于时间的计算比较乱,容易晕,请你跟好我的思路。
lockKey和requestId就不说了,和前面一样,这个方法里面要设置一个大的循环,每三秒循环一次,循环体内检测还有多久的剩余时长,也就是(当前系统时间 减去 添加锁时系统时间)如果这个时间小于存活时长的1/3,那我们就要给他添加时长,每次添加everyAddTime,记录添加的次数,添加次数达到maxIncreaseNumber 后不在添加,超时后释放锁。
此方法里可能会涉及到时间的递归赋值,尽量不要使用lambda表达式来启动线程。
/**
* 启动守护线程
*
* @param jedis Redis客户端
* @param lockKey key值
* @param requestId 请求标识
* @param addLockSystemTime 添加锁时系统时间
* @param maxIncreaseNumber 最大增加次数
* @param everyAddTime 每次增加时长
* @param expireTime 存活时长
*/
public static void startDaemonThread(Jedis jedis,String lockKey,
String requestId,Long addLockSystemTime,int maxIncreaseNumber,
int everyAddTime,int expireTime){}
使用redisson,这个是redis官方推荐的一个工具,很牛,关于分布式锁的这里面封装得很完整
首先从配置文件中获取redis连接信息,创建Redisson实例对象
然后在你的代码上方获取锁
//获取锁
RLock rLock= RedisHelper.getRedissonClient().getLock(“你需要锁的对象key值”);
尝试添加锁,tryLock()方法添加成功返回true,失败返回false,很多个线程同时来操作这个方法,但是只有第一个人能成功,添加锁得到true,其余的线程都会得到false。
第一个参数表示的是最多可以延长多久,这个延长时间你可以理解为续命,每次它检测到线程要死了,就马上给他续命,最多只能续到300秒(由第一个参数定义,我定义的是300秒),300秒之后不管程序是否执行完毕,都释放锁。第二个参数是他的存活时间,很多人会担心他的死锁问题,如果服务器突然宕机怎么办?第二个参数就是为了防止这种情况。如果该服务器宕机,从添加锁开始算30秒,如果没有人给它续命的话,他会自己释放锁。第三个参数就是前两个参数的单位。
try {
//tryLock(最长等待时间,存活时间,单位)
if (!rLock.tryLock(300,30, TimeUnit.SECONDS)){
logger.error("该电签正在进行中");
throw new MessageException("该电签正在进行中");
}catch(Exection e){
}finally{
//释放锁
rLock.unlock();
}