设计思路
- 加锁逻辑:使用
SETNX
命令尝试获取锁,如果获取成功则记录锁的持有次数并设置锁的持有者标识(如当前服务实例的唯一ID)。如果锁已被持有且持有者是当前服务实例,则增加持有次数,实现可重入。
- 解锁逻辑:当解锁时,判断持有次数,只有持有次数减为0时,才真正释放锁。
实现代码(Python示例)
import redis
import uuid
class ReentrantRedisLock:
def __init__(self, redis_client, lock_key, expire_time=10):
self.redis_client = redis_client
self.lock_key = lock_key
self.expire_time = expire_time
self.lock_holder = str(uuid.uuid4())
self.hold_count = 0
def acquire(self):
if self.hold_count > 0:
self.hold_count += 1
return True
result = self.redis_client.set(self.lock_key, self.lock_holder, ex=self.expire_time, nx=True)
if result:
self.hold_count = 1
return True
current_holder = self.redis_client.get(self.lock_key)
if current_holder and current_holder.decode('utf-8') == self.lock_holder:
self.hold_count += 1
return True
return False
def release(self):
if self.hold_count <= 0:
return
self.hold_count -= 1
if self.hold_count == 0:
self.redis_client.delete(self.lock_key)
# 使用示例
if __name__ == "__main__":
r = redis.Redis(host='localhost', port=6379, db=0)
lock = ReentrantRedisLock(r, 'test_lock')
if lock.acquire():
try:
# 业务逻辑
print('Lock acquired, doing business logic...')
# 再次获取锁
if lock.acquire():
try:
print('Re - acquired lock, doing more business logic...')
finally:
lock.release()
finally:
lock.release()
伪代码实现
// 初始化锁
function ReentrantRedisLock(redisClient, lockKey, expireTime) {
this.redisClient = redisClient
this.lockKey = lockKey
this.expireTime = expireTime
this.lockHolder = generateUniqueId()
this.holdCount = 0
}
// 获取锁
function acquire() {
if (this.holdCount > 0) {
this.holdCount = this.holdCount + 1
return true
}
result = this.redisClient.set(this.lockKey, this.lockHolder, ex = this.expireTime, nx = true)
if (result) {
this.holdCount = 1
return true
}
currentHolder = this.redisClient.get(this.lockKey)
if (currentHolder && currentHolder == this.lockHolder) {
this.holdCount = this.holdCount + 1
return true
}
return false
}
// 释放锁
function release() {
if (this.holdCount <= 0) {
return
}
this.holdCount = this.holdCount - 1
if (this.holdCount == 0) {
this.redisClient.delete(this.lockKey)
}
}