一.分布式锁的使用
使用方法:
/**
* 加锁,过期自动释放,时间单位传入
* @param lockKey
* @param unit 时间单位
* @param leaseTime 上锁后自动释放时间
* @return
*/
public RLock lock(String lockKey, TimeUnit unit, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, unit);
return lock;
}
/**
* 尝试获取锁
* @param lockKey
* @param unit 时间单位
* @param waitTime 最多等待时间
* @param leaseTime 上锁后自动释放时间
* @return
*/
public boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
return false;
}
}
/**
* 释放锁
* @param lockKey
*/
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
二.加锁源码解析:
public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException {
long threadId = Thread.currentThread().getId();
//加锁,并且返回过期时间,会调用tryAcquireAsync方法
Long ttl = this.tryAcquire(leaseTime, unit, threadId);
if (ttl != null) {
//获得过期时间
/**
订阅锁释放事件,并通过 await 方法阻塞等待锁释放,有效的解决了无效的锁申请浪费资源的问题:
基于信息量,当锁被其它资源占用时,当前线程通过 Redis 的 channel 订阅锁的释放事件
一旦锁释放会发消息通知待等待的线程进行竞争获取订阅.
*/
RFuture<RedissonLockEntry> future = this.subscribe(threadId);
//利用Redis的发布订阅来进行锁释放消息
this.commandExecutor.syncSubscription(future);
try {
//在最大等待时间内尝试加锁
while(true) {
ttl = this.tryAcquire(leaseTime, unit, threadId);
if (ttl == null) {
return;
}
//等待解锁信号量
if (ttl >= 0L) {
this.getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} else {
this.getEntry(threadId).getLatch().acquire();
}
}
} finally {
this.unsubscribe(future, threadId);
}
}
}
其中tryAcquire方法最后调用的是tryAcquireAsync,获取许可并且加锁方法
//加锁方法
private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, final long threadId) {
if (leaseTime != -1L) {
//如果有过期时间按照过期时间执行Redis lua脚本加锁
return this.tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
} else {
//没有过期时间,设置看门狗默认过期时间30秒
RFuture<Long> ttlRemainingFuture = this.tryLockInnerAsync(this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
//执行完lua脚本回调
ttlRemainingFuture.addListener(new FutureListener<Long>() {
public void operationComplete(Future<Long> future) throws Exception {
if (future.isSuccess()) {
Long ttlRemaining = (Long)future.getNow();
if (ttlRemaining == null) {
//开始看门狗定时任务,每个10秒续期key 30秒的存活时间
RedissonLock.this.scheduleExpirationRenewal(threadId);
}
}
}
});
return ttlRemainingFuture;
}
}
三.redisson分布式锁特性:
看门狗 :
定时任务,每10秒执行一次,检测Redis Key是否还存在,如果存在,续约30秒
订阅:
Semaphore信号量去做来释放和获取许可,并且通过Redis发布订阅功能来订阅锁的释放消息.
lua脚本:
来保证Redis命令的原子性
锁是hash结构
大key是锁的名称,小key是锁的标识,value是被重复加锁的次数
详细加锁过程中的发布订阅过程,可以查看其他博主的文章
https://www.jb51.net/article/234625.htm
redisson其他锁类型介绍