在使用Redis的分布式集群的时候,我们进行操作的时候需要对抢锁,也就是对线程的操作权限
我们在Redis中可以使用SetNX命令来进行锁争抢。
sexnx(Key,value),其中的key指的是锁的唯一标识,value姑且为1。
当锁抢成功,返回1则代表锁争抢成功。当然,我们接下来需要给锁一个有效时间,否则如果宕机的时候,进程挂掉,这个锁就一直不被释放,导致资源被占用。
所以我们可以设置过期时间expire(30).
有一个问题,因为争抢锁和设置过期时间的操作是分开的,所以在极端情况下,可能会出现抢到了锁,但是没有设置过期时间(在抢锁和设置过期时间之间服务器宕机)。也会发生资源一直被占用。
所以我们需要确保我们的抢锁和设置过期时间的操作是原子性的(其实就是transaction)。
所幸,我们可以使用set(key,value,expire,NX)来进行锁争抢。
锁的争抢完成了。那么我们在写代码的时候,有必要使用代码在任务完成之后删除当前锁。这就可能出现锁误删。
所以我们需要做判断,判断当前锁是否就是自己的那个线程中的锁。(保存线程ID可以使用抢锁时候的value)。
就是当线程A抢锁之后设置了30S的过期时间,但是他的任务用时可能45S,在30S的时候,锁过期了。线程B抢到了锁,在B执行的过程中,A任务执行完了,他就会删除锁,此时就会把B的锁删掉。这是不可以的。
但是这个不是原子性的。此时需要保证删除锁的操作也是原子性。
但是无论在什么情况下情况下,同一时间有多个线程在访问同样的代码块,这是我们不允许的。所以我们需要保证这个锁,要在任务时间内,不能被释放掉。所以我们需要开启一个守护线程。
开启一个守护线程,在我们的锁快要过期的时候,我们需要对锁进行续期,比如当剩下了1S的时候,我们可以再次设置过期时间为10S,一直循环往复。直到代码自己删除锁。
守护线程的实现是-->争抢到所以后,开启一个本地队列Task,去轮询这个Key(间隔N秒去检查这个Key的过期时间,然后根据需要去对锁进行续时),直到代码释放锁。