vb.net 线程偶尔不会自动关闭_Redis分布式锁:锁的续期,避免锁超时后导致多个线程获得锁...

3f5c0b237620e71cd85dd34c0a9e590b.png

使用现状

Redis分布式锁的基础内容,我们已经在Redis分布式锁:基于AOP和Redis实现的简易版分布式锁这篇文章中讲过了,也在文章中示范了正常的加锁和解锁方法。

分布式锁在之前的项目中一直运行良好,没有辜负我们的期望。

发现问题

但在最近查线上日志的时候偶然发现,有一个业务场景下,分布式锁偶尔会失效,导致有多个线程同时执行了相同的代码。

我们经过初步排查,定位到是因为在这段代码中间调用了第三方的接口导致。

因为业务代码耗时过长,超过了锁的超时时间,造成锁自动失效,然后另外一个线程意外的持有了锁。于是就出现了多个线程共同持有锁的现象。

解决方案

问题既然已经出现了,那么接下来我们就应该考虑解决方案了。

我们也曾经想过,是否可以通过合理地设置LockTime(锁超时时间)来解决这个问题?

但LockTime的设置原本就很不容易。LockTime设置过小,锁自动超时的概率就会增加,锁异常失效的概率也就会增加,而LockTime设置过大,万一服务出现异常无法正常释放锁,那么出现这种异常锁的时间也就越长。我们只能通过经验去配置,一个可以接受的值,基本上是这个服务历史上的平均耗时再增加一定的buff。

既然这条路走不通了,那么还有其他路可以走么?

当然还是有的,我们可以先给锁设置一个LockTime,然后启动一个守护线程,让守护线程在一段时间后,重新去设置这个锁的LockTime。

看起来很简单是不是?

但在实际操作中,我们要注意以下几点:

1、和释放锁的情况一致,我们需要先判断锁的对象是否没有变。否则会造成无论谁持有锁,守护线程都会去重新设置锁的LockTime。不应该续的不能瞎续。

2、守护线程要在合理的时间再去重新设置锁的LockTime,否则会造成资源的浪费。不能动不动就去续。

3、如果持有锁的线程已经处理完业务了,那么守护线程也应该被销毁。不能主人都挂了,守护者还在那里继续浪费资源。

代码实现

我们首先先生成一个内部类去实现Runnable,作为守护线程的参数。

public class SurvivalClamProcessor implements Runnable {

private static final int REDIS_EXPIRE_SUCCESS = 1;

SurvivalClamProcessor(String field, String key, String value, int lockTime) {

this.field = field;

this.key = key;

this.value = value;

this.lockTime = lockTime;

this.signal = Boolean.TRUE;

}

private String field;

private String key;

private String value;

private int lockTime;

//线程关闭的标记

private volatile Boolean signal;

void stop() {

this.signal = Boolean.FALSE;

}

@Override

public void run() {

int waitTime = lockTime * 1000 * 2 / 3;

while (signal) {

try {

Thread.sleep(waitTime);

if (cacheUtils.expandLockTime(field, key, value, lockTime) == REDIS_EXPIRE_SUCCESS) {

if (logger.isInfoEnabled()) {

logger.info("expandLockTime 成功,本次等待{}ms,将重置锁超时时间重置为{}s,其中field为{},key为{}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值