Spring Redis 插件中的分布式锁


org.springframework.data.redis.core.ValueOperations#setIfAbsent(K, V, long, java.util.concurrent.TimeUnit)
通过正文可见,其意义为【当key不存在时,设置这个key,并设置过期工夫】

为什么不应用 setnx 命令?

在通常的印象中,分布式锁命令不应该是setnx嘛?
setnx命令在Redis官网文档中的释义如下

要留神的是该命令并不蕴含过期工夫的设置,通常在设置一个分布式锁的时候都须要给锁加一个过期工夫避免程序异样而没有开释锁,那么如果应用setnx命令,就须要额定给这个key设置一个过期工夫,即expire命令,那么应用setnx命令去实现分布式锁就变成了2条命令,即
1.setnx key value
2.expire key seconds
此时加锁的命令就无奈保障原子性,如果在setnx胜利之后,零碎异样导致expire没有胜利执行,这个锁就变成了永不过期

为什么是 set 命令?

Redis在2.6.12版本中,为set命令退出了新的参数,使该命令间接反对了分布式锁,而加锁的命令为 set key value ex timeout nx,其含意为【当key不存在时,设置这个key,过期工夫为timeout,单位为秒】,此命令保障了操作的原子性,故Spring插件的分布式锁命令是set

加锁解锁中的并发问题

在上述的问题中,咱们曾经发现,加锁须要保障操作的原子性,那么在解锁的过程中呢?例如A线程加锁,过期30秒,而A线程执行的工夫曾经超过了30秒,锁曾经主动开释;此时B线程加锁胜利,开始执行,而A线程执行完结,删除了锁,但此时删除的锁理论为B线程加的;此时C线程又加锁胜利,开始执行;在此案例中,就无奈满足ABC三个线程的串行执行;
在此案例中,就引出了3个问题
1.须要以后线程只能解除本人加的锁
2.解锁须要保障原子性操作
3.锁过期工夫不够时须要续期操作

如何保障解锁的线程是加锁的线程?

线程应该应用本身的惟一标识来加锁,解锁时应用此惟一标识校验,如ThreadId,亦或uuid,只有保障不反复即可

如何保障解锁的原子性?

即便退出理解锁唯一性校验,在判断胜利开始解锁的霎时,也可能呈现B线程加锁胜利的状况,那么此时就须要保障判断可能解锁与解锁的这个过程是原子性的,具体能够参考org.redisson.RedissonLock#unlockInnerAsync

分布式锁如何续期?

加锁时,能够启动一个监督线程,通过定期轮循的形式来判断持有锁的线程有没有执行结束,如果没有执行完就主动续期,具体能够参考
org.redisson.RedissonLock#tryAcquireOnceAsync

在上述办法中,胜利设置了锁之后会启动一个主动renew(也就是续期)的办法,在此办法中,如果持有锁的线程没有执行完结,就会将这个锁缩短过期

参考资料:
https://blog.csdn.net/weixin_...
https://blog.csdn.net/weixin_...