本篇文章基于redisson-3.17.6版本源码进行分析
相比较Redisson可重入锁的加锁逻辑,释放锁的逻辑就相对简单一些。释放锁分为主动释放和自动释放两种方式。
主动释放
我们查看org.redisson.RedissonLock#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;
- }
- }
- // Future<Void> future = unlockAsync();
- // future.awaitUninterruptibly();
- // if (future.isSuccess()) {
- // return;
- // }
- // if (future.cause() instanceof IllegalMonitorStateException) {
- // throw (IllegalMonitorStateException)future.cause();
- // }
- // throw commandExecutor.convertException(future);
- }
同样采用异步的方式释放锁,unlockAsync():
- public RFuture<Void> unlockAsync(long threadId) {
- // 异步方式释放锁
- RFuture<Boolean> future = unlockInnerAsync(threadId);
- CompletionStage<Void> f = future.handle((opStatus, e) -> {
- // 取消看门狗定时任务
- cancelExpirationRenewal(threadId);
- if (e != null) {
- throw new CompletionException(e);
- }
- if (opStatus == null) {
- IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: "
- + id + " thread-id: " + threadId);
- throw new CompletionException(cause);
- }
- return null;
- });
- return new CompletableFutureWrapper<>(f);
- }
可以看到,首先调用unlockInnerAsync()方法释放锁,在释放锁之后,执行cancelExpirationRenewal(threadId)取消看门狗自动续期的定时任务。
释放锁核心逻辑:
- protected RFuture<Boolean> unlockInnerAsync(long threadId) {
- /**
- * Redisson解锁:通过LUA脚本释放锁,保证多个命令之间的原子性
- */
- return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
- "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
- "return nil;" +
- "end; " +
- "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
- "if (counter > 0) then " +
- "redis.call('pexpire', KEYS[1], ARGV[2]); " +
- "return 0; " +
- "else " +
- "redis.call('del', KEYS[1]); " +
- "redis.call('publish', KEYS[2], ARGV[1]); " +
- "return 1; " +
- "end; " +
- "return nil;",
- Arrays.asList(getRawName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
- }
Redisson可重入锁的释放还是通过LUA脚本实现,保证多个命令之间的原子性。
基本流程:
1、通过hexists指令判断锁是不是自己的,如果不是自己的锁,说明非法释放锁,返回nil,
2、如果是自己的锁,通过hincrby将锁的重入次数减1;
3、判断减1后的数是否大于0,如果减1后的数大于0,说明还没有完全释放锁,则重置锁的过期时间,并返回0;
4、如果减1后的数已经等于0,说明已经完全释放锁,则通过del指令释放锁,并通过publish发布一条消息,告诉其它订阅了这把锁的线程,我已经释放锁了,你们可以过来获取了;释放锁成功,返回1
5、其它情况,返回nil;
主动释放锁这块考虑的不仅仅是对 key 进行处理,因为可能存在重入锁,所以会先对 redis key 对应的 hash value 进行递减,相当于减去重入次数。
自动释放
当服务宕机时,看门狗不再看门,那么最多 30s 之后锁被自动释放;当设置锁的超时时间时,锁到了过期时间,自动释放;
到此这篇关于Redisson可重入锁解锁逻辑详细讲解的文章就介绍到这了,更多相关Redisson可重入锁解锁内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!