Redis 分布式锁可重入怎么实现?

168 阅读2分钟

在 Redis 中实现可重入的分布式锁通常需要使用一个类似于 ThreadLocal 的结构来记录每个线程持有锁的次数。由于 Redis 的单线程模型,分布式锁的可重入性通常是通过客户端在持有锁的时候记录持有次数,然后在释放锁的时候递减次数实现的。

以下是一个简单的 Java 示例,使用 Jedis 连接 Redis 实现可重入的分布式锁:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class ReentrantDistributedLock {

    private static final String LOCK_KEY = "my_lock";
    private static final ThreadLocal<Integer> lockCount = new ThreadLocal<>();

    private Jedis jedis;

    public ReentrantDistributedLock(Jedis jedis) {
        this.jedis = jedis;
    }

    public boolean tryLock() {
        String currentThreadId = String.valueOf(Thread.currentThread().getId());
        Long result = jedis.setnx(LOCK_KEY, currentThreadId);
        if (result == 1) {
            // 获取锁成功,初始化计数器
            lockCount.set(1);
            return true;
        } else {
            // 锁已经被其他线程占用,判断是否为当前线程
            String lockHolder = jedis.get(LOCK_KEY);
            if (currentThreadId.equals(lockHolder)) {
                // 是当前线程,增加计数器
                lockCount.set(lockCount.get() + 1);
                return true;
            } else {
                // 不是当前线程,获取锁失败
                return false;
            }
        }
    }

    public void unlock() {
        String currentThreadId = String.valueOf(Thread.currentThread().getId());
        String lockHolder = jedis.get(LOCK_KEY);
        if (currentThreadId.equals(lockHolder)) {
            // 是当前线程,减少计数器
            int count = lockCount.get();
            if (count == 1) {
                // 计数器为1,释放锁
                jedis.del(LOCK_KEY);
                lockCount.remove();
            } else {
                // 计数器大于1,减少计数器
                lockCount.set(count - 1);
            }
        }
    }

    public static void main(String[] args) {
        try (Jedis jedis = new Jedis("localhost", 6379)) {
            ReentrantDistributedLock lock = new ReentrantDistributedLock(jedis);

            // 第一次获取锁
            if (lock.tryLock()) {
                try {
                    System.out.println("Lock acquired");
                    // 在持有锁的情况下再次获取锁,计数器会增加
                    if (lock.tryLock()) {
                        try {
                            System.out.println("Reentrant lock acquired");
                            // 执行业务逻辑
                        } finally {
                            lock.unlock();
                            System.out.println("Reentrant lock released");
                        }
                    }
                } finally {
                    lock.unlock();
                    System.out.println("Lock released");
                }
            }
        }
    }
}

在这个例子中,tryLock 方法尝试获取锁,如果获取成功,会将当前线程ID记录为锁的持有者,并初始化计数器。如果锁已经被其他线程持有,会判断是否为当前线程,如果是,则增加计数器。unlock 方法用于释放锁,如果计数器为1,表示锁只被当前线程持有,直接释放锁;如果计数器大于1,表示有其他线程也持有锁,减少计数器。