1. Redisson简介
Redisson是一个基于Redis的Java客户端库,提供了丰富的分布式对象和服务(如分布式锁、信号量、Map等)。其核心优势在于简化分布式锁的实现,并解决了原生Redis分布式锁的常见问题(如死锁、误删锁等)。
2. Redisson分布式锁核心特性
2.1 锁类型
- 可重入锁(RLock):同一线程可重复获取同一把锁。
- 公平锁(FairLock):按请求顺序分配锁,避免饥饿现象。
- 联锁(MultiLock):一次性锁定多个资源。
- 红锁(RedLock):基于多Redis实例的高可用锁。
可重入锁(RLock)
可重入锁是Redisson最基础的锁类型,允许同一线程多次获取同一把锁而不会造成死锁。其核心原理是:
- 数据结构:使用Redis的Hash结构存储锁信息,key为锁名称,field为线程标识(UUID+threadId),value为重入次数
- 重入机制:当线程首次获取锁时,计数器设为1;同一线程再次获取时计数器递增,释放时递减,直到计数器归零才真正释放锁
- Lua脚本保证原子性:加锁和解锁操作都通过Lua脚本实现,确保操作的原子性
// 获取可重入锁示例
RLock lock = redisson.getLock("myLock");
lock.lock(); // 第一次获取锁
try {
lock.lock(); // 同一线程再次获取锁(重入)
// 执行业务逻辑
} finally {
lock.unlock(); // 释放锁(计数器减1)
lock.unlock(); // 完全释放锁(计数器归零)
}
公平锁(FairLock)
公平锁按照请求顺序分配锁资源,解决线程饥饿问题:
- 排队机制:使用Redis的List结构实现等待队列(redisson_lock_queue:{lockName}),ZSet结构记录超时时间(redisson_lock_timeout:{lockName})
- 公平获取:新线程获取锁时,必须检查自己是否是队列头部,只有队首线程才能获取锁
- 超时清理:定期清理队列中超时的线程请求,避免无效等待
// 公平锁使用示例
RLock fairLock = redisson.getFairLock("fairLock");
fairLock.lock();
try {
// 公平执行业务逻辑
} finally {
fairLock.unlock();
}
联锁(MultiLock)
联锁用于同时锁定多个资源:
- 原子性操作:将多个RLock组合成一个锁,所有锁必须全部获取成功才算成功
- 严格互斥:默认要求所有锁都必须成功获取(failedLocksLimit=0)
- 适用场景:如订单系统中需要同时锁定多个商品库存的场景
// 联锁使用示例
RLock lock1 = redisson.getLock("lock1");
RLock lock2 = redisson.getLock("lock2");
RLock lock3 = redisson.getLock("lock3");
RedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2, lock3);
multiLock.lock();
try {
// 同时操作多个受保护资源
} finally {
multiLock.unlock();
}
红锁(RedLock)
红锁是基于多Redis实例的高可用锁:
- 多数派原则:在N个独立Redis节点上获取锁,至少成功(N/2 +1)个才算获取成功
- 容错机制:即使部分节点故障,只要多数节点正常就能保证锁可用
- 解决主从问题:避免主从切换导致的锁失效问题
// 红锁使用示例
RLock lock1 = redisson1.getLock("lock1");
RLock lock2 = redisson2.getLock("lock2");
RLock lock3 = redisson3.getLock("lock3");
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
redLock.lock();
try {
// 执行高可用要求的业务逻辑
} finally {
redLock.unlock();
}
2.2 关键机制
- 看门狗自动续期:默认30秒检查一次,若业务未完成则延长锁过期时间。
- 锁超时释放:避免死锁,支持手动设置
leaseTime
。 - 非阻塞尝试:通过
tryLock(0, ...)
实现立即返回。
3. 代码示例
3.1 基础锁使用
// 1. 获取RedissonClient实例(需提前配置Redis连接)
RedissonClient redisson = Redisson.create(config);
// 2. 获取锁对象(锁键为"myLock")
RLock lock = redisson.getLock("myLock");
try {
// 3. 尝试获取锁,waitTime=0表示不重试,leaseTime=10秒表示锁自动释放时间
boolean isLocked = lock.tryLock(0, 10, TimeUnit.SECONDS);
if (isLocked) {
// 4. 执行业务逻辑
System.out.println("锁获取成功,执行业务...");
} else {
System.out.println("锁获取失败,直接返回");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("线程被中断");
} finally {
// 5. 释放锁(需判断当前线程是否持有锁)
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
3.2 注解式锁(Spring集成)
@Service
public class InventoryService {
private int stock = 100;
// 使用@Lock注解声明分布式锁(锁键为"decreaseStockLock")
@Lock(name = "decreaseStockLock")
public void decreaseStock() {
if (stock > 0) {
stock--;
System.out.println("库存扣减成功,剩余: " + stock);
}
}
}
4. 最佳实践
- 避免锁过期:若业务耗时不确定,建议不设置
leaseTime
(启用看门狗)。 - 防误删锁:在
unlock()
前校验锁持有者(isHeldByCurrentThread()
)。 - 非阻塞场景:使用
tryLock(0, ...)
快速失败。
5.对比总结
特性 | 可重入锁 | 公平锁 | 联锁 | 红锁 |
---|---|---|---|---|
核心特点 | 同一线程可重复获取 | 按请求顺序分配 | 同时锁定多个资源 | 多节点高可用 |
数据结构 | Hash结构 | Hash+List+ZSet | 多个RLock组合 | 多个独立节点锁 |
适用场景 | 一般并发控制 | 避免线程饥饿 | 多资源原子操作 | 高可用要求场景 |
性能影响 | 低 | 中等(需维护队列) | 高(需获取所有锁) | 高(多节点通信) |