1.获取锁对象
//获取一个可重入的锁对象,默认实现为RedissonLock
RedissonLock testLock = (RedissonLock)redissonClient.getLock("test");
2.对于当前锁对象加锁,然后释放锁
//阻塞等待直到获取锁
testLock.lock();
try {
System.out.println("获取到redis的锁");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
//判断当前持有锁的线程是否为当前线程
if(testLock.isHeldByCurrentThread()){
testLock.unlock();
}
}
3.先看lock()方法的实现
public void lock() {
try {
lock(-1, null, false);
} catch (InterruptedException e) {
throw new IllegalStateException();
}
}
/**
* @param leaseTime 申请锁的生存时间(-1为永久)
* @param unit 时间单位
* @param interruptibly 是否可中断
* @throws InterruptedException
*/
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
long threadId = Thread.currentThread().getId();
//首次尝试获取锁,返回值为当前锁的剩余生存时间
Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
//返回值为空说明当前线程成功,否则说明锁已经被占用
if (ttl == null) {
return;
}
//生成一个订阅锁释放的事件
RFuture<RedissonLockEntry> future = subscribe(threadId);
if (interruptibly) {
commandExecutor.syncSubscriptionInterrupted(future);
} else {
//同步执行事件
commandExecutor.syncSubscription(future);
}
//订阅完成后通过循环不断请求锁
try {
while (true) {
ttl = tryAcquire(-1, leaseTime, unit, threadId);
// 生存时间为空,拿到锁跳出
if (ttl == null) {
break;
}
// waiting for message
if (ttl >= 0) {
try {
//以阻塞的方式获取锁,最大等待时间为ttl(信号量)
future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
if (interruptibly) {
throw e;
}
future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
}
} else {
if (interruptibly) {
future.getNow().getLatch().acquire();
} else {
future.getNow().getLatch().acquireUninterruptibly();
}
}
}
} finally {
//取消订阅事件
unsubscribe(future, threadId);
}
}
下面看一下tryAcquire()方法
private Long tryAcquire(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
return get(tryAcquireAsync(waitTime, leaseTime, unit, threadId));
}
//阻塞等待RFuture任务返回结果
public <V> V get(RFuture<V> future) {
try {
future.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
if (future.isSuccess()) {
return future.getNow();
}
throw convertException(future);
}
private RFuture<Boolean> tryAcquireOnceAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
//生存时间不为-1时,表示请求获取一个有固定生存时间的锁,不需要看门狗的机制
if (leaseTime != -1) {
return tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
}
//异步调用RedisExecutor执行lua脚本实现加锁
RFuture<Boolean> ttlRemainingFuture = tryLockInnerAsync(waitTime,
commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(),
TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
//注册tryLockInnerAsync完成后的回调事件
//ttlRemaining是否加锁成功,e为异常信息
ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
if (e != null) {
return;
}
// lock acquired
if (ttlRemaining) {
//获取锁后将当前线程加入ExpirationEntry,以此来对线程持有的锁进行到期续约
scheduleExpirationRenewal(threadId);
}
});
return ttlRemainingFuture;
}
重点看下涉及看门狗机制的scheduleExpirationRenewal()方法
private void scheduleExpirationRenewal(long threadId) {
ExpirationEntry entry = new ExpirationEntry();
//将当前锁对象与ExpirationEntry 的映射关系加入Map
ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);
if (oldEntry != null) {
oldEntry.addThreadId(threadId);
} else {
//将当前线程加入类ExpirationEntry
//ExpirationEntry 包含两个重要属性Map<Long, Integer> threadIds和Timeout timeout
//threadIds保存续约的线程信息,timeout为netty的HashedWheelTimer时间轮的任务实例
entry.addThreadId(threadId);
renewExpiration();
}
}
//重点来了
private void renewExpiration() {
//释放锁后映射关系会被清除,所以要判断下是否还需要续约
ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ee == null) {
return;
}
//构建一个时间轮的延迟任务,该任务会在internalLockLeaseTime / 3毫秒后执行
//关于netty时间轮的实现后面再写一篇来说明
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ent == null) {
return;
}
Long threadId = ent.getFirstThreadId();
if (threadId == null) {
return;
}
//异步调用renewExpirationAsync执行lua脚本更新锁的生存时间
RFuture<Boolean> future = renewExpirationAsync(threadId);
future.onComplete((res, e) -> {
if (e != null) {
log.error("Can't update lock " + getName() + " expiration", e);
return;
}
if (res) {
//重复刷新生存时间
renewExpiration();
}
});
}
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
ee.setTimeout(task);
}
4.unlock()实现,基本就是将上面一个存储的线程信息和资源释放掉
public void unlock() {
try {
get(unlockAsync(Thread.currentThread().getId()));
} catch (RedisException e) {
if (e.getCause() instanceof IllegalMonitorStateException) {
throw (IllegalMonitorStateException) e.getCause();
} else {
throw e;
}
}
}
public RFuture<Void> unlockAsync(long threadId) {
RPromise<Void> result = new RedissonPromise<Void>();
//异步通过lua脚本释放锁
RFuture<Boolean> future = unlockInnerAsync(threadId);
future.onComplete((opStatus, e) -> {
//从EXPIRATION_RENEWAL_MAP移除线程信息
//把ExpirationEntry中TimeOut从时间轮的延迟任务中注销
cancelExpirationRenewal(threadId);
if (e != null) {
result.tryFailure(e);
return;
}
if (opStatus == null) {
IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: "
+ id + " thread-id: " + threadId);
result.tryFailure(cause);
return;
}
result.trySuccess(null);
});
return result;