3.对于线程 B,将超时时间 Tb = 201 通过 GetSet() 设置,由于锁超时时间已经被 A 重新设置,所以返回 T2 = 200,此时不满足条件 “T1 == T2”,获取锁失败。
// 获取分布式锁,需要考虑以下情况: // 1. 机器A获取到锁,但是在未释放锁之前,机器挂掉或者重启,会导致其它机器全部hang住,这时需要根据锁的超时时间,判断该锁是否需要重置; // 2. 当锁超时时,需要考虑两台机器同时去获取该锁,需要通过GETSET方法,让先执行该方法的机器获取锁,另外一台继续等待。 func GetDistributeLock(key string, expireTime int64) bool { currentTime := time.Now().Unix() expires := currentTime + expireTime redisAlias := "jointly" // 堆代码 duidaima.com // 1.获取锁,并将value值设置为锁的超时时间 redisRet, err := redis.SetNx(redisAlias, key, expires) if nil == err && utils.MustInt64(1) == redisRet { // 成功获取到锁 return true } // 2.当获取到锁的机器突然重启&挂掉时,就需要判断锁的超时时间,如果锁超时,新的机器可以重新获取锁 // 2.1 获取锁的超时时间 currentLockTime, err := redis.GetKey(redisAlias, key) if err != nil { return false } // 2.2 当"锁的超时时间"大于等于"当前时间",证明锁未超时,直接返回 if utils.MustInt64(currentLockTime) >= currentTime { return false } // 2.3 将最新的超时时间,更新到锁的value值,并返回旧的锁的超时时间 oldLockTime, err := redis.GetSet(redisAlias, key, expires) if err != nil { return false } // 2.4 当锁的两个"旧的超时时间"相等时,证明之前没有其它机器进行GetSet操作,成功获取锁 // 说明:这里存在并发情况,如果有A和B同时竞争,A会先GetSet,当B再去GetSet时,oldLockTime就等于A设置的超时时间 if utils.MustString(oldLockTime) == currentLockTime { return true } return false }删除锁逻辑:
// 删除分布式锁 // @return bool true-删除成功;false-删除失败 func DelDistributeLock(key string) bool { redisAlias := "jointly" redisRet := redis.Del(redisAlias, key) if redisRet != nil { return false } return true }业务逻辑:
func DoProcess(processId int) { fmt.Printf("启动第%d个线程\n", processId) redisKey := "redis_lock_key" for { // 获取分布式锁 isGetLock := GetDistributeLock(redisKey, 10) if isGetLock { fmt.Printf("Get Redis Key Success, id:%d\n", processId) time.Sleep(time.Second * 3) // 删除分布式锁 DelDistributeLock(redisKey) } else { // 如果未获取到该锁,为了避免redis负载过高,先睡一会 time.Sleep(time.Second * 1) } } }最后起个 10 个多线程,去执行这个 DoProcess():
func main() { // 初始化资源 var group string = "group" var name string = "name" var host string // 初始化资源 host = "http://ip:port" _, err := xrpc.NewXRpcDefault(group, name, host) if err != nil { panic(fmt.Sprintf("initRpc when init rpc failed, err:%v", err)) } redis.SetRedis("louzai", "redis_louzai") // 开启10个线程,去抢Redis分布式锁 for i := 0; i <= 9; i ++ { go DoProcess(i) } // 避免子线程退出,主线程睡一会 time.Sleep(time.Second * 100) return }程序跑了100 s,我们可以看到,每次都只有 1 个线程获取到锁,分别是 2、1、5、9、3,执行结果如下:
启动第0个线程 启动第6个线程 启动第9个线程 启动第4个线程 启动第5个线程 启动第2个线程 启动第1个线程 启动第8个线程 启动第7个线程 启动第3个线程 Get Redis Key Success, id:2 Get Redis Key Success, id:2 Get Redis Key Success, id:1 Get Redis Key Success, id:5 Get Redis Key Success, id:5 Get Redis Key Success, id:5 Get Redis Key Success, id:5 Get Redis Key Success, id:5 Get Redis Key Success, id:5 Get Redis Key Success, id:5 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:9 Get Redis Key Success, id:3 Get Redis Key Success, id:3 Get Redis Key Success, id:3 Get Redis Key Success, id:3 Get Redis Key Success, id:3