一、示例代码(Redisson MultiLock 使用)

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
 
public class MultiLockExample {
    public static void main(String[] args) throws InterruptedException {
        // 配置多个 RedissonClient(连接不同 Redis 节点)
        Config config1 = new Config();
        config1.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson1 = Redisson.create(config1);
 
        Config config2 = new Config();
        config2.useSingleServer().setAddress("redis://127.0.0.1:6380");
        RedissonClient redisson2 = Redisson.create(config2);
 
        Config config3 = new Config();
        config3.useSingleServer().setAddress("redis://127.0.0.1:6381");
        RedissonClient redisson3 = Redisson.create(config3);
 
        // 获取三个节点的 RLock
        RLock lock1 = redisson1.getLock("myLock");
        RLock lock2 = redisson2.getLock("myLock");
        RLock lock3 = redisson3.getLock("myLock");
 
        // MultiLock:需要同时获得多个节点的锁
        RLock multiLock = Redisson.createMultiLock(lock1, lock2, lock3);
 
        try {
            // 尝试加锁(等待 5 秒,租约时间 10 秒)
            if (multiLock.tryLock(5, 10, java.util.concurrent.TimeUnit.SECONDS)) {
                System.out.println("成功获取到 MultiLock,执行业务逻辑...");
                Thread.sleep(3000);
            } else {
                System.out.println("未能获取 MultiLock");
            }
        } finally {
            // 解锁
            multiLock.unlock();
        }
 
        // 关闭客户端
        redisson1.shutdown();
        redisson2.shutdown();
        redisson3.shutdown();
    }
}

二、源码分析(Redisson MultiLock)

1. MultiLock 类结构

源码位置:org.redisson.RedissonMultiLock
实现了 RLock 接口,本质是一个组合锁

public class RedissonMultiLock extends RedissonLock implements RLock {
    final List<RLock> locks = new ArrayList<RLock>();
 
    public RedissonMultiLock(RLock... locks) {
        this.locks.addAll(Arrays.asList(locks));
    }
}

2. 加锁逻辑

核心方法:tryLock

@Override
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
    long newLeaseTime = -1;
    if (leaseTime != -1) {
        newLeaseTime = unit.toMillis(waitTime);
    }
 
    long time = System.currentTimeMillis();
    long remainTime = unit.toMillis(waitTime);
 
    int failedLocksLimit = locks.size() - (locks.size() / 2 + 1);
    int failedLocks = 0;
 
    for (ListIterator<RLock> iterator = locks.listIterator(); iterator.hasNext();) {
        RLock lock = iterator.next();
        boolean lockAcquired;
        try {
            // 在剩余时间内尝试加锁
            lockAcquired = lock.tryLock(remainTime, newLeaseTime, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            failedLocks++;
            continue;
        }
 
        if (!lockAcquired) {
            failedLocks++;
        }
 
        // 如果失败次数超过限制,直接解锁已获取的锁
        if (failedLocks > failedLocksLimit) {
            unlockInner(iterator);
            return false;
        }
 
        remainTime -= (System.currentTimeMillis() - time);
        if (remainTime <= 0) {
            unlockInner(iterator);
            return false;
        }
    }
 
    return true;
}

3. 关键点解析

  • 分布式一致性目标
    MultiLock 本质是实现 Redlock 算法(由 Redis 作者提出)。
    需要在 N 个 Redis 节点 中至少在 大多数(N/2+1)节点 成功加锁,才算成功。

  • 失败回滚
    如果某个节点加锁失败(返回 false 或抛异常),并且失败次数超过阈值,就会解锁之前已成功加锁的节点,保证不会出现部分加锁的情况。

  • 超时控制
    waitTime 内不断尝试,超过时间后放弃。

  • leaseTime
    如果传入 -1,会启动看门狗自动续期机制;否则会设置指定过期时间。


4. 解锁逻辑

unlock() 会遍历所有子锁,逐一解锁:

@Override
public void unlock() {
    for (RLock lock : locks) {
        try {
            lock.unlock();
        } catch (Exception e) {
            // ignore (例如某个节点宕机)
        }
    }
}

保证即使部分节点异常,也能尽可能释放锁。


三、总结

  1. 使用方式

    • Redisson.createMultiLock(lock1, lock2, lock3)

    • 多节点同时加锁,确保分布式环境下的安全性。

  2. 源码核心逻辑

    • 遍历多个子锁依次执行 tryLock

    • 必须在多数节点成功加锁才算成功(>= N/2+1)。

    • 如果失败超过阈值 → 回滚已加的锁。

    • 解锁时遍历释放所有子锁。

  3. 对应场景

    • Redis 主从切换、单点故障时,仍能保证分布式锁的正确性。

    • 避免单点 Redis 宕机导致的锁失效问题。

sequenceDiagram
    participant Thread as 应用线程
    participant Redis1 as Redis 节点1
    participant Redis2 as Redis 节点2
    participant Redis3 as Redis 节点3

    Note over Thread,Redis3: MultiLock (Redlock 算法) 加锁过程

    Thread->>Redis1: tryLock(key, leaseTime)
    Redis1-->>Thread: 成功 (OK)

    Thread->>Redis2: tryLock(key, leaseTime)
    Redis2-->>Thread: 成功 (OK)

    Thread->>Redis3: tryLock(key, leaseTime)
    Redis3-->>Thread: 失败 (锁已被占用)

    Note over Thread,Redis3: 已成功节点数 = 2 (>= N/2+1),加锁成功

    Thread->>Thread: 返回 true,业务逻辑执行中

    Thread->>Redis1: unlock(key)
    Thread->>Redis2: unlock(key)
    Thread->>Redis3: unlock(key) (可能忽略失败)