Redis可重入锁实战,分享多线程竞争问题的解决方案

文章导读
解决方案:在Redis中实现可重入锁的关键是使用setnx命令结合线程ID或唯一标识来判断是否为同一线程重入。核心代码如下:public boolean lock(String key, long timeout) { String threadId = Thread.currentThread().getId() + "";r/> String value = threadId + Sys
📋 目录
  1. A 来源1
  2. B 来源2
  3. C 来源3
  4. D 来源4
  5. E 来源5
  6. F 来源6
A A

解决方案:在Redis中实现可重入锁的关键是使用setnx命令结合线程ID或唯一标识来判断是否为同一线程重入。核心代码如下:
public boolean lock(String key, long timeout) {
String threadId = Thread.currentThread().getId() + "";r/> String value = threadId + System.currentTimeMillis();
if (redis.setnx(key, value)) {
redis.expire(key, 30);
return true;
}
String currentValue = redis.get(key);
if (threadId.equals(currentValue.split("-")[0])) {
// 重入,递增锁计数
redis.hincrBy(key + ":count", threadId, 1);
return true;
}
return false;
}
public void unlock(String key) {
String threadId = Thread.currentThread().getId() + "";r/> long count = redis.hincrBy(key + ":count", threadId, -1);
if (count <= 0) {
redis.del(key);
redis.del(key + ":count");
}
}

来源1

多线程竞争问题在高并发场景下很常见,使用Redis分布式锁可以有效解决,但普通锁不支持重入。我们需要记录锁的持有者(线程ID)和重入次数。每次获取锁时,先用setnx尝试加锁,如果失败则检查当前锁持有者是否是自己,如果是则递增重入计数器。释放时递减计数器,为0时才真正删除锁。这样就实现了可重入性。

来源2

Redis可重入锁的实现原理:使用Hash结构存储锁信息,key为锁名,field为线程标识,value为重入次数。加锁逻辑:setnx设置初始锁,如果已存在且field匹配当前线程,hincrBy递增value。解锁:hincrBy递减value <=0 时del整个hash。这样多线程下同一线程可多次获取同一锁,避免死锁。

Redis可重入锁实战,分享多线程竞争问题的解决方案

来源3

实战中遇到的问题:多线程下如果不检查重入,会导致同一线程无法连续调用加锁函数。解决方案是用UUID+线程ID作为value,解析时匹配线程ID部分。还需设置watchdog线程续期,防止业务超时导致锁误释。代码中结合Lua脚本原子执行set和expire,避免并发问题。

来源4

在Spring中集成Redis重入锁:自定义RedissonReentrantLock,但简化版可以用RedissonClient.getLock(key).lock(),它内置支持重入。底层用Redis的hash存储{threadId: count}。多线程测试:100线程同时抢锁,只有持有者能重入,其他线程等待或失败。

Redis可重入锁实战,分享多线程竞争问题的解决方案

来源5

注意事项:1. value必须唯一包含线程标识。2. 使用pipeline或Lua确保原子性。3. 锁超时时间要合理,防止死锁。4. 解锁时必须验证持有者,避免误删他人锁。实战案例:电商秒杀库存扣减,多线程调用扣库存服务,重入锁确保一致性。

Redis可重入锁实战,分享多线程竞争问题的解决方案

来源6

完整代码示例:
class ReentrantLock {
private static final String LOCK_KEY = "lock:%s";r/> private static final String COUNT_KEY = "count:%s";r/> public boolean acquire(String resource) {
String key = String.format(LOCK_KEY, resource);
String id = UUID.randomUUID().toString();
if (jedis.setnx(key, id)) {
jedis.setnx(String.format(COUNT_KEY, resource), "1");
jedis.expire(key, 30);
return true;
}
// 检查重入
return false; // 简化
}
}

FAQ
Q: 为什么需要可重入锁?
A: 因为业务方法可能递归调用或链式调用,同一线程需要多次加锁,普通锁会阻塞自己。
Q: Redis锁如何防止误删?
A: 加锁时设置value为线程ID,解锁时比对value匹配才删除。
Q: 如何处理锁超时?
A: 加锁后设置expire,并用后台线程续期。
Q: Redisson和自定义哪个好?
A: Redisson成熟稳定,自定义适合学习和轻量场景。
Q: 多机环境如何用?
A: Redis集群模式,确保所有节点可见锁。