可重入性
可重入性是指线程在持有锁的情况下再次请求加锁,如果一个锁支持同一个线程的多次加锁,那么这个锁就是可重入的。比如 Java 语言里有个 ReentrantLock 就是可重入锁。
Redis 分布式锁如果要支持可重入,需要对客户端的 set 方法进行包装,使用线程的 Threadlocal 变量 存储当前持有锁的计数。
示例
package com.example.demo.book;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: acton_zhang
* @Date: 2023/4/15 11:28 下午
* @Version 1.0
* Redis可重入分布式锁
*/
public class RedisWithReentrantLock {
//使用线程的Threadlocal存储当前持有锁的计数
private ThreadLocal<Map> lockers = new ThreadLocal<>();
private Jedis jedis;
public RedisWithReentrantLock(Jedis jedis) {
this.jedis = jedis;
}
private boolean _lock(String key, String value, long ttl) {
SetParams params = new SetParams();
//使用:set key value ex ttl nx 命令。一般来说,超时时间ttl要大于业务时间
params.nx().ex(ttl);
return jedis.set(key, value, params) != null;
}
private void _unlock(String key) {
jedis.del(key);
}
private Map<String, Integer> currentLockers() {
Map<String, Integer> refs = lockers.get();
if (refs != null) {
return refs;
}
lockers.set(new HashMap());
return lockers.get();
}
//获取锁
public boolean lock(String key, String value, long ttl) {
Map<String, Integer> refs = currentLockers();
Integer refCnt = refs.get(key);
if (refCnt != null) {
refs.put(key, refCnt + 1);
return true;
}
boolean ok = this._lock(key, value, ttl);
if (!ok) {
return false;
}
refs.put(key, 1);
return true;
}
//释放锁
public boolean unlock(String key) {
Map<String, Integer> refs = currentLockers();
Integer refCnt = refs.get(key);
if (refCnt == null) {
return false;
}
refCnt -= 1;
if (refCnt > 0) {
refs.put(key, refCnt);
} else {
refs.remove(key);
this._unlock(key);
}
return true;
}
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.auth("123456");
RedisWithReentrantLock rlock = new RedisWithReentrantLock(jedis);
System.out.println(rlock.lock("testlock", "value", 5l));
System.out.println(rlock.lock("testlock", "value", 5l));
System.out.println(rlock.unlock("testlock"));
System.out.println(rlock.unlock("testlock"));
}
}