Redisson分布式锁的原理 锁续期 看门狗 死锁问题

使用Redis锁,会有业务未执行完,锁过期的问题,可以采用Redisson锁解决。

Redisson锁有两种模式

1.固定有效期的锁:超过有效期leaseTime后,自动释放锁。

    public void lock(long leaseTime, TimeUnit unit) {
        try {
            this.lockInterruptibly(leaseTime, unit);
        } catch (InterruptedException var5) {
            Thread.currentThread().interrupt();
        }
    }

2.没有有效期的锁:默认30秒,然后采用Watchdog进行续期,直到业务逻辑执行完毕。

    public void lock() {
        try {
            this.lockInterruptibly();
        } catch (InterruptedException var2) {
            Thread.currentThread().interrupt();
        }
    }

加锁方法最终调用这个方法:

    private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, final long threadId) {
        if (leaseTime != -1L) {
            // 有有效期的
            return this.tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
        } else {
            // 没有有效期的,这里启动了一个守护线程对锁续期
            RFuture<Long> ttlRemainingFuture = this.tryLockInnerAsync(this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
            ttlRemainingFuture.addListener(new FutureListener<Long>() {
                public void operationComplete(Future<Long> future) throws Exception {
                    if (future.isSuccess()) {
                        Long ttlRemaining = (Long)future.getNow();
                        if (ttlRemaining == null) {
                            RedissonLock.this.scheduleExpirationRenewal(threadId);
                        }
                    }
                }
            });
            return ttlRemainingFuture;
        }
    }

看门狗续期方法:

    private void scheduleExpirationRenewal(final long threadId) {
        if (!expirationRenewalMap.containsKey(this.getEntryName())) {
            Timeout task = this.commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
                public void run(Timeout timeout) throws Exception {
                    // 执行lua 进行续期
                    RFuture<Boolean> future = RedissonLock.this.renewExpirationAsync(threadId);
                    future.addListener(new FutureListener<Boolean>() {
                        public void operationComplete(Future<Boolean> future) throws Exception {
                            RedissonLock.expirationRenewalMap.remove(RedissonLock.this.getEntryName());
                            if (!future.isSuccess()) {
                                RedissonLock.log.error("Can't update lock " + RedissonLock.this.getName() + " expiration", future.cause());
                            } else {
                                if ((Boolean)future.getNow()) {
                                    RedissonLock.this.scheduleExpirationRenewal(threadId);
                                }
                            }
                        }
                    });
                }
                // 每隔internalLockLeaseTime/3 = 10秒检查一次
            }, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);
            if (expirationRenewalMap.putIfAbsent(this.getEntryName(), new RedissonLock.ExpirationEntry(threadId, task)) != null) {
                task.cancel();
            }
        }
    }

释放锁方法:

    public void unlock() {
        try {
            this.get(this.unlockAsync(Thread.currentThread().getId()));
        } catch (RedisException var2) {
            if (var2.getCause() instanceof IllegalMonitorStateException) {
                throw (IllegalMonitorStateException)var2.getCause();
            } else {
                throw var2;
            }
        }
    }
    public RFuture<Void> unlockAsync(final long threadId) {
        final RPromise<Void> result = new RedissonPromise();
        // 执行lua脚本 删除key
        RFuture<Boolean> future = this.unlockInnerAsync(threadId);
        future.addListener(new FutureListener<Boolean>() {
            public void operationComplete(Future<Boolean> future) throws Exception {
                if (!future.isSuccess()) {
                    // 删除expirationRenewalMap缓存,停止watch dog机制
                    RedissonLock.this.cancelExpirationRenewal(threadId);
                    result.tryFailure(future.cause());
                } else {
                    Boolean opStatus = (Boolean)future.getNow();
                    if (opStatus == null) {
                        IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: " + RedissonLock.this.id + " thread-id: " + threadId);
                        result.tryFailure(cause);
                    } else {
                        if (opStatus) {
                            RedissonLock.this.cancelExpirationRenewal((Long)null);
                        }
                        result.trySuccess((Object)null);
                    }
                }
            }
        });
        return result;
    }
    void cancelExpirationRenewal(Long threadId) {
        RedissonLock.ExpirationEntry task = (RedissonLock.ExpirationEntry)expirationRenewalMap.get(this.getEntryName());
        if (task != null && (threadId == null || task.getThreadId() == threadId)) {
            expirationRenewalMap.remove(this.getEntryName());
            task.getTimeout().cancel();
        }
    }

释放锁的操作一定要放到 finally {},保证释放锁的方法unlock()一定被执行,另外unlock()底层的cancelExpirationRenewal()也保证了一定释放锁成功,不会出现死锁现象。

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值