muitLock和redLock

1. 不加锁:会超扣(问题演示)

// 伪代码:两个线程同时转账,都基于旧余额判断

classAccount{

intbalance;

}

voidtransfer(Account from,Account to,int amount){

// 危险:读、判断、写中间没有互斥

if(from.balance>= amount){

from.balance-= amount;// 线程1、线程2可能同时进来

to.balance+= amount;

}

}

// A=100

// 线程1:A→B 转 80

// 线程2:A→C 转 80

// 两边都读到 100,都认为够 → 实际转出 160,A 却可能只剩 20


2. 一把一把加,顺序反了:死锁

RLocklockA=redisson.getLock("account:A");

RLocklockB=redisson.getLock("account:B");

// 线程1:A → B

newThread(()->{

lockA.lock();

sleep(100);// 模拟业务间隙

lockB.lock();// 等 B

try{

// 转账

}finally{

lockB.unlock();

lockA.unlock();

}

}).start();

// 线程2:B → A(顺序反了)

newThread(()->{

lockB.lock();

sleep(100);

lockA.lock();// 等 A

try{

// 转账

}finally{

lockA.unlock();

lockB.unlock();

}

}).start();

// 结果:线程1 拿着 A 等 B,线程2 拿着 B 等 A → 死锁


3. MultiLock:正确写法(推荐看这个)

importorg.redisson.api.RLock;

importorg.redisson.api.RedissonClient;

publicclassTransferService{

privatefinalRedissonClientredisson;

publicTransferService(RedissonClientredisson){

this.redisson= redisson;

}

/**

* 从 fromUser 转 amount 到 toUser

*/

publicbooleantransfer(LongfromUser,LongtoUser,intamount){

// 两把账户锁

RLocklockFrom=redisson.getLock("lock:account:"+ fromUser);

RLocklockTo=redisson.getLock("lock:account:"+ toUser);

// 联锁:要么都加上,要么整体失败(内部处理顺序/释放)

RLockmultiLock=redisson.getMultiLock(lockFrom, lockTo);

booleanlocked=false;

try{

// 最多等 3 秒抢锁;不指定 leaseTime → 可走看门狗

locked =multiLock.tryLock(3,TimeUnit.SECONDS);

if(!locked){

returnfalse;// 抢锁失败,稍后再试

}

// ===== 临界区:同一时刻只有一个转账在改这两个账户 =====

intfromBalance=getBalance(fromUser);// 查库/查缓存

if(fromBalance < amount){

returnfalse;// 余额不足

}

deduct(fromUser, amount);// A 减钱

add(toUser, amount);// B 加钱

saveLog(fromUser, toUser, amount);

returntrue;

}catch(InterruptedExceptione){

Thread.currentThread().interrupt();

returnfalse;

}finally{

if(locked){

multiLock.unlock();// 两把锁一起放

}

}

}

// 下面这些你用 MyBatis/MySQL 实现即可

privateintgetBalance(LonguserId){return100;}

privatevoiddeduct(LonguserId,intamount){}

privatevoidadd(LonguserId,intamount){}

privatevoidsaveLog(Longfrom,Longto,intamount){}

}


对照理解

不加锁:

两个转账同时读到余额100 → 超扣

乱加锁:

线程1锁A等B,线程2锁B等A → 死锁

MultiLock:

同时申请 A+B,都成功才进临界区

改完一起 unlock → 安全且不易死锁


和你秒杀的对应关系

// 你项目:锁的是「用户」,防止一人多单

RLocklock=redisson.getLock("lock:order"+ userId);

lock.tryLock(1,TimeUnit.SECONDS);

// 转账例子:锁的是「两个账户」,防止余额算错

RLockmulti=redisson.getMultiLock(lockA, lockB);

multi.tryLock(3,TimeUnit.SECONDS);

本质一样:改共享数据前先互斥,改完再放。

红锁呢

红锁 = RedLock,是 Redis 作者提出的「多 Redis 节点」分布式锁方案。别和 MultiLock 搞混。


和 MultiLock 的区别(先分清)

MultiLock(联锁)

RedLock(红锁)

锁几个 key

多个不同业务 key(账户A、账户B)

同一个业务,但写到多个 Redis

锁几台 Redis

通常 1 台

多台独立 Redis(如 5 台)

解决啥

同时保护多个资源、防死锁

单机 Redis 挂了/主从切换丢锁

MultiLock:1个Redis里 → lockA + lockB

RedLock :5个Redis里 → 同一把锁各写一遍,超过半数成功才算拿到


为什么要有红锁

单 Redis 锁的坑:

1. 客户端在「主节点」加锁成功

2. 主还没同步给从

3. 主挂了,从被提升成新主

4. 新主上没有这把锁

5. 另一个客户端又能加锁 → 两个人同时持锁

RedLock 想用 多数派 降低这种风险。


RedLock 怎么工作

假设 5 个独立 Redis(不是主从,是 5 个 master):

1. 记录当前时间

2. 依次向 5 个节点执行 SET key value NX PX

3. 如果「成功节点数 ≥ 3」(超过半数)

且「总耗时 < 锁有效期」

→ 加锁成功

4. 否则 → 失败,并把已加上的节点上的锁全删掉

5. 解锁 → 每个节点都删(用 Lua 判断 value)

Redis1 ✅ Redis2 ✅ Redis3 ❌ Redis4 ✅ Redis5 ❌

成功 3 个 ≥ 3 → 拿到锁


Redisson 代码

// 假设配了多个 RedissonClient,指向不同 Redis

RLocklock1=redisson1.getLock("lock:order:"+ userId);

RLocklock2=redisson2.getLock("lock:order:"+ userId);

RLocklock3=redisson3.getLock("lock:order:"+ userId);

// 红锁:同一把锁,多个 Redis 实例

RLockredLock=redisson1.getRedLock(lock1, lock2, lock3);

booleanlocked=false;

try{

// 等最多 1 秒;锁持有 10 秒(也可不写 leaseTime 用看门狗)

locked =redLock.tryLock(1,10,TimeUnit.SECONDS);

if(!locked){

returnResult.fail("系统繁忙");

}

// 业务:下单 / 转账 ...

returndoBusiness();

}finally{

if(locked){

redLock.unlock();

}

}

底层思路:每个节点都是一把普通 RLock,RedLock 负责「多数成功才算成功」。


红锁一定安全吗?

不一定。 业界有争议(Martin Kleppmann 质疑过)。

仍可能出问题的情况:

  1. 机器时钟跳变,锁提前过期
  2. 客户端 Full GC 卡住,锁过期了还以为自己有锁
  3. 本质仍依赖「过期时间」,不是强一致共识

所以:

  • 多数业务:单 Redis + Redisson + 数据库唯一约束/幂等 就够
  • 真要强一致:更倾向 ZooKeeper / etcd,或加 fencing token

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值