基于redis的分布式锁 RedissonLock解锁异常解决


回到顶部

问题现象

在并发操作的场景下(对业务接口连续请求三次),使用基于redis的分布式锁 RedissonLock解锁时抛出异常。

回到顶部

问题复现代码

复制代码

public boolean testLock(Integer type) {
        RLock lock = redissonClient.getLock("testLock" + 22);
        log.info("testLock:1:" + lock.toString() + ",interrupted:" +
                Thread.currentThread().isInterrupted() + ",hold:" +
                lock.isHeldByCurrentThread() + ",threadId:" +
                Thread.currentThread().getId() + ",redissonClient:{}" + redissonClient);

        try {
            boolean res = lock.tryLock(3, 60, TimeUnit.SECONDS);
            log.info("testLock:2:" + lock.toString() + ",interrupted:" +
                    Thread.currentThread().isInterrupted() + ",hold:"
                    + lock.isHeldByCurrentThread() + ",threadId:" + Thread.currentThread().getId()
                    + ",redissonClient:{}" + redissonClient + ",res:{}" + res);
            if (res) {
                // 业务代    
                Thread.sleep(4000L);
            }
        } catch (InterruptedException e) {
            log.error("分布式锁{}获取失败", lock.getName());
            throw new BusinessProcessFailException("分布式锁【" + lock.getName() + "】获取失败", ErrorCode.SYSTEM_EXCEPTION.getCode());
        } finally {
            log.info("testLock:3:" + lock.toString() + ",interrupted:" +
                    Thread.currentThread().isInterrupted() + ",hold:" +
                    lock.isHeldByCurrentThread() + ",threadId:" + Thread.currentThread().getId() + ",redissonClient:{}" + redissonClient);
            lock.unlock();
        }
        return false;
    }

复制代码

回到顶部

具体的异常信息如下

java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 1f24378c-5456-4321-827a-bc0a7515ec5d thread-id: 227
at org.redisson.RedissonLock.unlock(RedissonLock.java:366) ~[redisson-2.10.5.jar:na]
 

回到顶部

排查过程

业务代码中添加日志打印

log.info("testLock:1:" + lock.toString()                                   // lock锁对象地址

+ ",interrupted:" + Thread.currentThread().isInterrupted()        // 当前线程是否中断

+ ",hold:" + lock.isHeldByCurrentThread()                            // 当前请求的线程是否是锁对象的持有者

+ ",threadId:" + Thread.currentThread().getId()                     // 线程ID

+ ",redissonClient:{}" + redissonClient)                                // redissonClient地址

+ ",res:{}" + res);                                                              // 线程加锁结果

日志

  • testLock:1:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:false,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80               
  • testLock:2:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:true,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80,res:{}true
  • testLock:1:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80
  • testLock:1:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:false,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80
  • testLock:2:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80,res:{}false
  • testLock:3:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80
  • testLock:3:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:true,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80
  • testLock:2:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:true,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80,res:{}true
  • testLock:3:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:true,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80

通过日志数据可知

线程 242、235、244 使用的是同一个redissonClient

线程 242、235、244 使用的lock锁对象不是同一个,242是6132b8ee,235是33e44749,244是6da98d58

线程执行流程

testLock:1:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:false,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80                    // 线程242构建锁对象

testLock:2:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:true,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80,res:{}true      // 线程 242 加锁成功

testLock:1:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80                    // 线程235构建锁对象

testLock:1:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:false,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80                    // 线程 244 构建锁对象

testLock:2:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80,res:{}false    // 线程235加锁失败

testLock:3:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80                   // 线程235 最终会走到finally 执行解锁,但是解锁失败(该线程并没有获取到锁)

testLock:3:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:true,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80                     // 线程 242 解锁成功

testLock:2:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:true,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80,res:{}true      // 线程244加锁成功

testLock:3:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:true,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80                     // 线程244解锁成功

解决方法

解锁时添加判断

if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}

lock.isLocked():判断要解锁的key是否已被锁定。

lock.isHeldByCurrentThread():判断要解锁的key是否被当前线程持有。

回到顶部

结论

1、只有加锁成功才需要解锁

2、自己加的锁,自己解,不能解别人的锁

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明
Redisson是一个基于Redis分布式锁实现框架。它提供了简单而强大的API,使得在分布式环境中使用分布式锁变得非常方便。 要使用Redisson实现分布式锁,首先需要引入相应的依赖。可以通过Maven或Gradle将Redisson添加到项目中。 接下来,可以通过以下步骤实现分布式锁: 1. 创建Redisson客户端连接:使用Redisson提供的Config对象配置Redis连接信息,并创建RedissonClient实例。 2. 获取分布式锁对象:通过RedissonClient的getLock()方法获取分布式锁对象。可以指定一个唯一的名称作为锁的标识。 3. 加锁:通过调用分布式锁对象的lock()方法进行加锁操作。如果获取到锁,则可以执行对应的业务逻辑;否则,等待锁释放。 4. 解锁:在业务逻辑执行完毕后,需要调用分布式锁对象的unlock()方法进行解锁操作,释放锁资源。 以下是一个简单的示例代码: ```java // 1. 创建Redisson客户端连接 Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379"); RedissonClient redisson = Redisson.create(config); // 2. 获取分布式锁对象 RLock lock = redisson.getLock("myLock"); // 3. 加锁 lock.lock(); try { // 执行业务逻辑 // ... } finally { // 4. 解锁 lock.unlock(); } // 关闭Redisson客户端连接 redisson.shutdown(); ``` 需要注意的是,在使用分布式锁时,加锁和解锁的操作应该配对出现,并且解锁操作应该在finally块中执行,以确保在发生异常的情况下,锁能够被正确地释放。 通过Redisson,你可以方便地实现分布式锁,确保在分布式环境中的数据一致性和并发控制。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值