SpringRedis插件org.springframework.data.redis.core.ValueOperations#setIfAbsent(K,V,long,java.util.concurrent.TimeUnit)中的分布式锁通过注释可以看到,其含义是[当key不存在,设置这个key并设置过期时间]为什么不用setnx命令呢?在通常的印象中,分布式锁命令不应该是setnx吗?Redis官方文档中setnx命令的定义如下。需要注意的是,该命令不包括过期时间的设置。通常在设置分布式锁的时候,需要给锁加上一个过期时间,防止程序出现异常而没有释放锁。那么如果使用setnx命令,需要给key额外设置一个过期时间,也就是expire命令,那么使用setnx命令实现分布式锁就变成了两条命令,即1.setnxkeyvalue2.expire此时的keyseconds不能保证加锁命令的原子性。如果setnx成功后,系统异常导致expire未能成功执行,锁将永不过期。为什么是set命令?Redis在2.6.12版本的set命令中加入了新的参数,使得该命令直接支持分布式锁,加锁命令为setkeyvalueextimeoutnx,意思是【当key不存在时,设置这个key,过期时间为timeout,单位为秒],该命令保证了操作的原子性,所以Spring插件的分布式加锁命令是setlock和unlock中的并发问题。在上面的问题中,我们发现,加锁需要保证操作的原子性,那么在解锁的过程中呢?比如线程A加锁过期30秒,线程A执行时间已经超过30秒,锁已经自动释放;此时线程B加锁成功并开始执行,而线程A执行完毕并删除锁,但此时删除的锁实际上是B线程添加的;此时C线程成功锁定并开始执行;在这种情况下,无法满足ABC三个线程的串行执行;在这种情况下,三个问题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_...
