上一篇文档我们探讨了redis实现分布式锁以及它遇到的问题,为了解决这些可能会写很多代码,一不小心可能就会产生bug。其实这些问题Redisson都帮我们解决了,那么今天我们来探讨一下Redisson实现分布式锁
首先来看一下代码示例:
public class RedissonLock {
public static void main(String[] args) {
Config config = new Config();
// 单机模式
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redissonClient = Redisson.create(config);
RLock rLock = redissonClient.getLock("key");
try {
// 超时获取锁(100毫秒超时时间、10毫秒锁的时间)
rLock.tryLock(100,10,TimeUnit.MILLISECONDS);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rLock.unlock();
redissonClient.shutdown();
}
}
}
比起redis去实现分布式锁要考虑很多场景,这种写法是不是代码简洁了很多。核心代码其实就三行,如果我们跟踪进去就会看到下面的源码
return this.commandExecutor.evalWriteAsync(this.getName(), LongCodec.INSTANCE, command,
"if (redis.call('exists', KEYS[1]) == 0)
then redis.call('hset', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil; end;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1)
then redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil; end;
return redis.call('pttl', KEYS[1]);",
Collections.singletonList(this.getName()), new Object[]{this.internalLockLeaseTime, this.getLockName(threadId)});
我们从中还是可以看懂一点,其实现思路还是那个思路,只不过Redisson用lua脚本把我们实现了,lua脚本具有原子性,操作比我们用代码实现更加严谨,最主要Redisson被广泛使用,是经过验证的,不会有什么bug。
我们之前提到过一个问题,这个锁的超时时间设置多少合适呢?如果时间到了业务代码没执行完该怎么办?Redisson是这么实现的,获取锁的线程会启动一个后台线程去监视这个线程,每隔一段时间就去轮训一次看看这个线程有没有执行结束,如果没有结束,那么延长时间为超时时间的3分之一秒,如果你没有设置超时时间那么Redisson默认设置30秒,那么这个轮训间隔时间就是10秒,RedissonLock.this.scheduleExpirationRenewal(threadId);就是后台线程启动的定时任务