Redisson
这个是一个Redis框架,提供了各种的分布式服务
Redisson的使用
使用也很简单
- 注入Redisson
- redisson.getLock:获取锁,获取锁还没有进行上锁,返回一个RLock
- RLock.lock:进行加锁,本质上还是执行redis的setnx命令,默认30S过期时间
- RLock.unlock:进行解锁
下面来一个小Demo
public class ControllerOne {
private Redisson redisson;
public String doSomething(){
//锁的名字
String lockName = "stock-name";
//使用redisson来进行获取锁,这一步只是获取还没有进行加锁
RLock lock = redisson.getLock(lockName);
try{
String result = "result";
//进行加锁
lock.lock();
//do something
return result;
}finally {
//进行解锁
lock.unlock();
}
}
}
可以看到用起来十分简单,下面就分析一下源码
源码分析
首先认识一下整体的架构和原理是怎样的
其实整体的架构也就是解决了上一章留下的锁续命问题
- 当一个线程去尝试获取锁的时候,会采用自旋的方式去一致尝试加锁
- 加锁成功,就会开启一个后台线程,后台线程会每隔10S检查线程是否还持有锁(进行锁续命)
- 如果还持有,则会延长锁的时间
- 如果不持有,就不会进行延长
- 加锁失败,自旋回去继续加锁
- 加锁成功,就会开启一个后台线程,后台线程会每隔10S检查线程是否还持有锁(进行锁续命)
认识了原理之后,我们先认识一个概念,LUA脚本
LUA脚本其实是一门脚本语言,Redisson其实就是利用LUA脚本来执行redis命令的,并且Redis在执行LUA脚本时,会当作一个原子性命令去执行
getLock
先看第一条代码

这一行代码其实就是实例化想要获取的锁,比如锁的key,因为锁也是一个对象(RLock),在面向对象的语言中必须要先进行实例化才能使用

进入到Redisson里面,它其实就是实例化了一个RedissonLock,并且给了两个参数
- CommandExecutor:命令执行器
- name:锁的key
具体的构造方法如下
public RedissonLock(CommandAsyncExecutor commandExecutor, String name) {
//进行实例化父类
super(commandExecutor, name);
//装入命令执行器
this.commandExecutor = commandExecutor;
//设置锁的默认过期时间,默认30S
this.internalLockLeaseTime = commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout();
//获取订阅频道
this.pubSub = commandExecutor.getConnectionManager().getSubscribeService().getLockPubSub();
}
从构造方法可以看到,其实所有的配置都在Config类这里

至此,一把锁就被实例化了,产生了一个RedissonLock对象
但RedissonLock是一个具体实现,对外的接口却是RLock,所以先看看RLock的构造

可以看到Rlock多继承了Lock与RLockAsync
- Lock:JDK的concurrent.locks包下的,也就是JDK自身的Lock,让RLock满足了JDK锁的规范,并且Lock在JDK的实现只有ReentrantLock也就是可重入锁,所以RLock是支持可重入的
- RLockAsync:Redisson进行扩展的功能,添加了异步锁的功能,所以RLock也支持异步锁
再来看看其实现类

可以看到,Redisson支持的锁都实现了RLock,所以都有可重入、异步的功能
RedissonLock对象
我们先看一下它整体的架构

可以看到RedissonLock其实是一个继承的演化过程
- RedissonObject:抽象类,最底层的与Redis进行交互的功能
- RedissonExpirable:抽象类,拥有RedissonObject功能的同时,自身提供设置过期时间的一些支持
- RedissonBaseLock:抽象类,拥有RedissonExpirable功能的同时,自身提供续锁续命功能(开启一个线程任务去不断续命)
- RedissonLock:拥有BaseLock功能,实现了可重入锁(实现了),与服务器连接断开自动移除锁,并且提供一系列锁的服务,异步上锁等

对于RedissonLock其成员变量

- CommandAsyncExecutor:命令异步执行器
- LockPubSub:订阅的频道(之后会讲这个频道的作用)
- internalLockLeaseTime:拥有锁的时间
对于RedissonLock的构造方法(tryLock的目的其实就是构造一把锁出来)
只有一个构造方法
public RedissonLock(CommandAsyncExecutor commandExecutor, String name) {
//构造父类
super(commandExecutor, name);
//注入命令执行器
this.commandExecutor = commandExecutor;
//设置锁的过期时间,上面也截图显示这为30S
this.internalLockLeaseTime = commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout();
//注入订阅服务(Redis的订阅功能)
this.pubSub = commandExecutor.getConnectionManager().getSubscribeService().getLockPubSub();
}
整体的结构已经看完了,下面就分析一下,lock方法的细节
lock
lock方法其实是重写RLock的

对外提供的Lock方法有两种
- 默认的无参lock方法,无参的就是使用默认的过期时间,也就是30S
- 带有过期时间和时间单位参数的lock方法
具体调用的lock方法,可以看到这是一个private方法,需要给三个参数
- leaseTime:过期时间
- TimeUnit:时间单位
- interruptiably:是否优先响应中断,线程是可以将其他线程设为中断状态(interrupted),而在分布式中,不同实例之间的线程不能通信,所以需要借助Redis的发布订阅功能来实现通信,也就是线程在频道中发布让那个线程interrupted(实现的本质是AQS)
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
//获取当前线程ID
long threadId = Thread.currentThread().getId();
//尝试进行加锁
Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
// lock acquired
//如果加锁成功,return结束
if (ttl == null) {
return;
}
//加锁失败,为了避免空转,通过订阅频道看能不能获取锁
//所以要先进行订阅频道
//订阅频道
RFuture<RedissonLockEntry> future = subscribe(threadId);
//判断是否优先响应中断
if (interruptibly) {
//优先响应中断并开启开门狗
commandExecutor

本文详细解读了Redisson框架中的分布式锁使用,涉及源码分析,包括getLock过程、Watchdog机制、锁续命与renewExpiration等,以及RFuture接口的订阅与取消订阅操作。重点讲解了LUA脚本在加锁中的原子性保障和AQS在优先响应中断的应用。
最低0.47元/天 解锁文章
2713

被折叠的 条评论
为什么被折叠?



