Redis distributed reentrant lock implementation

 

Reentrant lock

 

A thread can acquire the lock multiple times, indicating that the lock is reentrant. Let's implement Redisdistributed reentrant locks. We can use thread ThreadLocalvariables to store the current lock count.

 

Core code

 

The following code is not perfect, but provides an idea.

@Component
public class RedisWithReentrantLock {

    @Autowired
    private RedisTemplate redisTemplate;


    private static final String REDIS_VALUE = "r_lock";

    //ThreadLocal存储,同线程持有锁(key)相同,计数+1
    private ThreadLocal<Map<String, Integer>> lockers = new ThreadLocal<>();

    //相当于setnx + 超时时间
    //这里还有个问题,业务中有可能持有锁的那段代码没有执行完,锁就超时了,这里需要设计一个看门狗线程,去监听线程是否持有锁
    private boolean _lock(String key) {
        return redisTemplate.opsForValue().setIfAbsent(key, REDIS_VALUE, 20, TimeUnit.SECONDS);
    }

    //释放锁
    private void _unlock(String key) {
        redisTemplate.delete(key);
    }

    //获取当前线程持有锁的Map, key为redis的key,value为重入的次数
    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) {
        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);
        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 = refCnt - 1;
        if ((refCnt > 0)) {
            refs.put(key, refCnt);
        } else {
            refs.remove(key);
            this._unlock(key);
        }
        return true;
    }


}

 

 

test

 

As shown above: when a thread Thread-14acquires a lock, it can acquire it multiple times. If it is not released, the main thread cannot acquire the lock. Of course, when writing code, you can adjust the code structure to avoid using reentrant locks.

reference:

"The Core Principles and Citation Practice of Redis In-Depth Adventure"-Qian Wenpin

Guess you like

Origin blog.csdn.net/wujialv/article/details/108414850