一、示例代码(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 (例如某个节点宕机)
}
}
}保证即使部分节点异常,也能尽可能释放锁。
三、总结
-
使用方式:
-
Redisson.createMultiLock(lock1, lock2, lock3) -
多节点同时加锁,确保分布式环境下的安全性。
-
-
源码核心逻辑:
-
遍历多个子锁依次执行
tryLock。 -
必须在多数节点成功加锁才算成功(>= N/2+1)。
-
如果失败超过阈值 → 回滚已加的锁。
-
解锁时遍历释放所有子锁。
-
-
对应场景:
-
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) (可能忽略失败)