面试题答案
一键面试- 实现思路:
- 为每个获取锁的线程关联一个标识,比如线程ID。
- 在锁的value中记录获取锁的线程ID和重入次数。
- 当线程尝试获取锁时,首先判断锁是否存在。若不存在,则获取锁并初始化线程ID和重入次数为1。若锁已存在且线程ID与当前线程ID一致,则增加重入次数。
- 释放锁时,若重入次数大于1,则只减少重入次数;若重入次数为1,则删除锁。
- 涉及的Redis操作:
- 获取锁:
- 使用
SETNX
(SET if Not eXists)命令尝试设置锁的键值对。如果锁不存在,SETNX
返回1,说明获取锁成功,此时设置锁的value为当前线程ID:1。例如,假设锁键为lock_key
,线程ID为thread_id
,可以使用如下伪代码:
- 使用
- 获取锁:
if (redis.call('SETNX', 'lock_key', 'thread_id:1') == 1) {
return true;
}
- 如果`SETNX`返回0,说明锁已存在。此时使用`GET`命令获取锁的value,判断value中的线程ID是否与当前线程ID一致。如果一致,使用`INCRBY`命令将重入次数加1。例如:
local value = redis.call('GET', 'lock_key')
if (value ~= nil and string.sub(value, 1, string.find(value, ':') - 1) == 'thread_id') {
redis.call('INCRBY', 'lock_key', 1)
return true;
}
return false;
- 释放锁:
- 使用
GET
命令获取锁的value,判断value中的线程ID是否与当前线程ID一致。如果一致,使用DECRBY
命令将重入次数减1。
- 使用
local value = redis.call('GET', 'lock_key')
if (value ~= nil and string.sub(value, 1, string.find(value, ':') - 1) == 'thread_id') {
local new_count = redis.call('DECRBY', 'lock_key', 1)
if (new_count == 0) {
redis.call('DEL', 'lock_key')
}
return true;
}
return false;
上述代码使用Redis Lua脚本来保证操作的原子性,避免并发情况下的竞争问题。