Redisson
使用redis实现了分布式锁
分布式锁特性
互斥性
多个线程并发时,互相排斥,需要互斥则使用相同的key即可
getName为key名称,getLockName记录了线程
数据结构类似如下:
// redis key
{
"线程id": 1
}
加锁成功脚本中都会返回nil,否则会返回剩余过期时间毫秒数
防死锁
防止加了锁,程序奔溃后没有释放锁的情况,导致其他线程永远无法成功加锁
通过给key添加过期时间来解决,但是如果过期时间到了,业务代码还没执行结束,这种情况redisson使用watch dog来解决,watch dog见下文
可重入
例如递归调用,如果当前线程加锁成功,当前线程递归调用该方法,应该可以成功加锁,而不是已经加了锁则报错加锁失败
lua脚本的第二个if中,判断了ARGV[2]即线程id存在情况下,对value进行次数增加1,所以可重入通过value记录了重入的次数
锁误删
即只能是加锁的人来解锁或者防死锁情况自动解锁
锁误删举例:
- 线程A进行加锁,并加锁成功,继续执行业务代码
- redis服务器宕机,并且没有持久化
- redis服务器恢复
- 线程B进行加锁,并加锁成功(此时Redis服务器中的锁事线程B加的)
- 线程A业务执行完成,执行锁释放,这事如果没有锁误删限制,那么线程A就会释放线程B加的锁
实现:
依旧通过线程id,作为hash的key的方式来防止误删
Watch Dog 看门狗
启动看门狗的代码如上图
使用commandExecutor.getConnectionManager().newTimeout
的方式添加了一个延时任务
使用timer添加刚才定义的Task延时任务,看看timer是什么
HashedWheelTimer
是netty提供的一个基于时间轮算法的延迟队列工具,用于延迟执行任务
时间轮算法
参考原文https://www.cnblogs.com/lihao007/p/10588072.html
HashedWheelTimer(
ThreadFactory threadFactory,
long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection,
long maxPendingTimeouts)
threadFactory:线程工厂,用于创建工作线程,并且只会创建一个
tickDuration + unit:在每一个格子上的停留时间
ticksPerWheel:时间轮的格子数,每一个格子里都是一个Timeout任务队列bucket,双向链表结构
leakDetection:默认为true,开启泄漏检测
maxPendingTimeouts:最大挂起Timeouts任务数量
案例:
如图,一个轮分成8个bucket,每一个tick需要1秒,现在有一个延迟任务为延迟20秒后执行,则需要tick20次,1轮需要tick8次,则20/8 = 2轮,这个任务在添加到这个轮(wheel)中时,通过hash算法计算出应该放到哪一个目标bucket中并将计算出的2轮设置为该任务的剩余轮数,当指针每tick一次,落到这个bucket中时,会将bucket中的timeout(我们自己的延迟任务)的剩余轮数-1,当剩余轮数小于等于0的时候就执行该任务并从bucket中移除